Merge pull request #238013 from tweag/lib.path.removePrefix

`lib.path.removePrefix`: init

Changed files
+71 -1
lib
path
+53
lib/path/default.nix
···
concatMap
foldl'
take
+
drop
;
inherit (lib.strings)
···
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
+
/*
+
Remove the first path as a component-wise prefix from the second path.
+
The result is a normalised subpath string, see `lib.path.subpath.normalise`.
+
+
Laws:
+
+
- Inverts `append` for normalised subpaths:
+
+
removePrefix p (append p s) == subpath.normalise s
+
+
Type:
+
removePrefix :: Path -> Path -> String
+
+
Example:
+
removePrefix /foo /foo/bar/baz
+
=> "./bar/baz"
+
removePrefix /foo /foo
+
=> "./."
+
removePrefix /foo/bar /foo
+
=> <error>
+
removePrefix /. /foo
+
=> "./foo"
+
*/
+
removePrefix =
+
path1:
+
assert assertMsg
+
(isPath path1)
+
"lib.path.removePrefix: First argument is of type ${typeOf path1}, but a path was expected.";
+
let
+
path1Deconstructed = deconstructPath path1;
+
path1Length = length path1Deconstructed.components;
+
in
+
path2:
+
assert assertMsg
+
(isPath path2)
+
"lib.path.removePrefix: Second argument is of type ${typeOf path2}, but a path was expected.";
+
let
+
path2Deconstructed = deconstructPath path2;
+
success = take path1Length path2Deconstructed.components == path1Deconstructed.components;
+
components =
+
if success then
+
drop path1Length path2Deconstructed.components
+
else
+
throw ''
+
lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".'';
+
in
+
assert assertMsg
+
(path1Deconstructed.root == path2Deconstructed.root) ''
+
lib.path.removePrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
+
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
+
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
+
joinRelPath components;
/* Whether a value is a valid subpath string.
+18 -1
lib/path/tests/unit.nix
···
{ libpath }:
let
lib = import libpath;
-
inherit (lib.path) hasPrefix append subpath;
+
inherit (lib.path) hasPrefix removePrefix append subpath;
cases = lib.runTests {
# Test examples from the lib.path.append documentation
···
testHasPrefixExample4 = {
expr = hasPrefix /. /foo;
expected = true;
+
};
+
+
testRemovePrefixExample1 = {
+
expr = removePrefix /foo /foo/bar/baz;
+
expected = "./bar/baz";
+
};
+
testRemovePrefixExample2 = {
+
expr = removePrefix /foo /foo;
+
expected = "./.";
+
};
+
testRemovePrefixExample3 = {
+
expr = (builtins.tryEval (removePrefix /foo/bar /foo)).success;
+
expected = false;
+
};
+
testRemovePrefixExample4 = {
+
expr = removePrefix /. /foo;
+
expected = "./foo";
};
# Test examples from the lib.path.subpath.isValid documentation