summary refs log tree commit diff
diff options
context:
space:
mode:
authorRobert Hensing <roberth@users.noreply.github.com>2023-07-19 17:28:05 +0200
committerGitHub <noreply@github.com>2023-07-19 17:28:05 +0200
commit814f067760d029f532f990974a6df240d4320105 (patch)
tree7a2e2329be756f263a0d207b71287fd4d2fffe7f
parentc7d007c7f868b32a1c0f3a7f2f96462c356497f8 (diff)
parent6626d8cc4de80f063da7ab871ecdc66f40f28e0b (diff)
downloadnixpkgs-814f067760d029f532f990974a6df240d4320105.tar
nixpkgs-814f067760d029f532f990974a6df240d4320105.tar.gz
nixpkgs-814f067760d029f532f990974a6df240d4320105.tar.bz2
nixpkgs-814f067760d029f532f990974a6df240d4320105.tar.lz
nixpkgs-814f067760d029f532f990974a6df240d4320105.tar.xz
nixpkgs-814f067760d029f532f990974a6df240d4320105.tar.zst
nixpkgs-814f067760d029f532f990974a6df240d4320105.zip
Merge pull request #238013 from tweag/lib.path.removePrefix
`lib.path.removePrefix`: init
-rw-r--r--lib/path/default.nix53
-rw-r--r--lib/path/tests/unit.nix19
2 files changed, 71 insertions, 1 deletions
diff --git a/lib/path/default.nix b/lib/path/default.nix
index 936e9b03025..3a871bc0528 100644
--- a/lib/path/default.nix
+++ b/lib/path/default.nix
@@ -20,6 +20,7 @@ let
     concatMap
     foldl'
     take
+    drop
     ;
 
   inherit (lib.strings)
@@ -217,6 +218,58 @@ in /* No rec! Add dependencies on this file at the top. */ {
               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.
 
diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix
index 9c5b752cf64..3e4b216f099 100644
--- a/lib/path/tests/unit.nix
+++ b/lib/path/tests/unit.nix
@@ -3,7 +3,7 @@
 { 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
@@ -57,6 +57,23 @@ let
       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
     testSubpathIsValidExample1 = {
       expr = subpath.isValid null;