summaryrefslogtreecommitdiff
path: root/lib/path
diff options
context:
space:
mode:
authorSilvan Mosberger <silvan.mosberger@tweag.io>2023-01-18 18:15:55 +0100
committerSilvan Mosberger <silvan.mosberger@tweag.io>2023-02-13 14:01:17 +0100
commit1a2c2846b0933b596c51e964acb8a45ab9a13691 (patch)
treecccb126ed6f1c06e3cd9caecf5c45a788c474329 /lib/path
parentMerge pull request #208887 from tweag/lib.path.append (diff)
downloadnixpkgs-1a2c2846b0933b596c51e964acb8a45ab9a13691.tar.gz
lib.path.subpath.join: init
This function can be used to safely join subpaths together
Diffstat (limited to 'lib/path')
-rw-r--r--lib/path/default.nix77
-rw-r--r--lib/path/tests/unit.nix30
2 files changed, 107 insertions, 0 deletions
diff --git a/lib/path/default.nix b/lib/path/default.nix
index 075e2fc0d137..a4a08668ae62 100644
--- a/lib/path/default.nix
+++ b/lib/path/default.nix
@@ -15,6 +15,9 @@ let
last
genList
elemAt
+ all
+ concatMap
+ foldl'
;
inherit (lib.strings)
@@ -190,6 +193,80 @@ in /* No rec! Add dependencies on this file at the top. */ {
subpathInvalidReason value == null;
+ /* Join subpath strings together using `/`, returning a normalised subpath string.
+
+ Like `concatStringsSep "/"` but safer, specifically:
+
+ - All elements must be valid subpath strings, see `lib.path.subpath.isValid`
+
+ - The result gets normalised, see `lib.path.subpath.normalise`
+
+ - The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`
+
+ Laws:
+
+ - Associativity:
+
+ subpath.join [ x (subpath.join [ y z ]) ] == subpath.join [ (subpath.join [ x y ]) z ]
+
+ - Identity - `"./."` is the neutral element for normalised paths:
+
+ subpath.join [ ] == "./."
+ subpath.join [ (subpath.normalise p) "./." ] == subpath.normalise p
+ subpath.join [ "./." (subpath.normalise p) ] == subpath.normalise p
+
+ - Normalisation - the result is normalised according to `lib.path.subpath.normalise`:
+
+ subpath.join ps == subpath.normalise (subpath.join ps)
+
+ - For non-empty lists, the implementation is equivalent to normalising the result of `concatStringsSep "/"`.
+ Note that the above laws can be derived from this one.
+
+ ps != [] -> subpath.join ps == subpath.normalise (concatStringsSep "/" ps)
+
+ Type:
+ subpath.join :: [ String ] -> String
+
+ Example:
+ subpath.join [ "foo" "bar/baz" ]
+ => "./foo/bar/baz"
+
+ # normalise the result
+ subpath.join [ "./foo" "." "bar//./baz/" ]
+ => "./foo/bar/baz"
+
+ # passing an empty list results in the current directory
+ subpath.join [ ]
+ => "./."
+
+ # elements must be valid subpath strings
+ subpath.join [ /foo ]
+ => <error>
+ subpath.join [ "" ]
+ => <error>
+ subpath.join [ "/foo" ]
+ => <error>
+ subpath.join [ "../foo" ]
+ => <error>
+ */
+ subpath.join =
+ # The list of subpaths to join together
+ subpaths:
+ # Fast in case all paths are valid
+ if all isValid subpaths
+ then joinRelPath (concatMap splitRelPath subpaths)
+ else
+ # Otherwise we take our time to gather more info for a better error message
+ # Strictly go through each path, throwing on the first invalid one
+ # Tracks the list index in the fold accumulator
+ foldl' (i: path:
+ if isValid path
+ then i + 1
+ else throw ''
+ lib.path.subpath.join: Element at index ${toString i} is not a valid subpath string:
+ ${subpathInvalidReason path}''
+ ) 0 subpaths;
+
/* Normalise a subpath. Throw an error if the subpath isn't valid, see
`lib.path.subpath.isValid`
diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix
index a1a45173a909..61c4ab4d6f2e 100644
--- a/lib/path/tests/unit.nix
+++ b/lib/path/tests/unit.nix
@@ -107,6 +107,36 @@ let
expected = true;
};
+ # Test examples from the lib.path.subpath.join documentation
+ testSubpathJoinExample1 = {
+ expr = subpath.join [ "foo" "bar/baz" ];
+ expected = "./foo/bar/baz";
+ };
+ testSubpathJoinExample2 = {
+ expr = subpath.join [ "./foo" "." "bar//./baz/" ];
+ expected = "./foo/bar/baz";
+ };
+ testSubpathJoinExample3 = {
+ expr = subpath.join [ ];
+ expected = "./.";
+ };
+ testSubpathJoinExample4 = {
+ expr = (builtins.tryEval (subpath.join [ /foo ])).success;
+ expected = false;
+ };
+ testSubpathJoinExample5 = {
+ expr = (builtins.tryEval (subpath.join [ "" ])).success;
+ expected = false;
+ };
+ testSubpathJoinExample6 = {
+ expr = (builtins.tryEval (subpath.join [ "/foo" ])).success;
+ expected = false;
+ };
+ testSubpathJoinExample7 = {
+ expr = (builtins.tryEval (subpath.join [ "../foo" ])).success;
+ expected = false;
+ };
+
# Test examples from the lib.path.subpath.normalise documentation
testSubpathNormaliseExample1 = {
expr = subpath.normalise "foo//bar";