about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorRobert Hensing <roberth@users.noreply.github.com>2023-07-27 11:05:48 +0200
committerGitHub <noreply@github.com>2023-07-27 11:05:48 +0200
commit399ac29381a5abc7bc17d41ff96f00b17716727c (patch)
treef067ddf7dfa90636d4cc114b326dd095b5ede0e3 /lib
parent6fbe34cb763925313bfd0a29ad3b900ba96f269c (diff)
parentd7bf0d777a0edba5a6c87d59e9b7516c796fcd1f (diff)
downloadnixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar.gz
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar.bz2
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar.lz
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar.xz
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.tar.zst
nixlib-399ac29381a5abc7bc17d41ff96f00b17716727c.zip
Merge pull request #244358 from tweag/lib.path.parts
`lib.path.splitRoot`: init
Diffstat (limited to 'lib')
-rw-r--r--lib/path/default.nix49
-rw-r--r--lib/path/tests/unit.nix19
2 files changed, 67 insertions, 1 deletions
diff --git a/lib/path/default.nix b/lib/path/default.nix
index 3a871bc05283..24a7f85affc1 100644
--- a/lib/path/default.nix
+++ b/lib/path/default.nix
@@ -271,8 +271,57 @@ in /* No rec! Add dependencies on this file at the top. */ {
               second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
         joinRelPath components;
 
+  /*
+  Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
+  The result is an attribute set with these attributes:
+  - `root`: The filesystem root of the path, meaning that this directory has no parent directory.
+  - `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.
+
+  Laws:
+  - [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:
+
+        p ==
+          append
+            (splitRoot p).root
+            (splitRoot p).subpath
+
+  - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:
+
+        dirOf (splitRoot p).root == (splitRoot p).root
+
+  Type:
+    splitRoot :: Path -> { root :: Path, subpath :: String }
+
+  Example:
+    splitRoot /foo/bar
+    => { root = /.; subpath = "./foo/bar"; }
+
+    splitRoot /.
+    => { root = /.; subpath = "./."; }
+
+    # Nix neutralises `..` path components for all path values automatically
+    splitRoot /foo/../bar
+    => { root = /.; subpath = "./bar"; }
+
+    splitRoot "/foo/bar"
+    => <error>
+  */
+  splitRoot = path:
+    assert assertMsg
+      (isPath path)
+      "lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
+    let
+      deconstructed = deconstructPath path;
+    in {
+      root = deconstructed.root;
+      subpath = joinRelPath deconstructed.components;
+    };
+
   /* Whether a value is a valid subpath string.
 
+  A subpath string points to a specific file or directory within an absolute base directory.
+  It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.
+
   - The value is a string
 
   - The string is not empty
diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix
index 3e4b216f099f..8bfb6f201219 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 removePrefix append subpath;
+  inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
 
   cases = lib.runTests {
     # Test examples from the lib.path.append documentation
@@ -74,6 +74,23 @@ let
       expected = "./foo";
     };
 
+    testSplitRootExample1 = {
+      expr = splitRoot /foo/bar;
+      expected = { root = /.; subpath = "./foo/bar"; };
+    };
+    testSplitRootExample2 = {
+      expr = splitRoot /.;
+      expected = { root = /.; subpath = "./."; };
+    };
+    testSplitRootExample3 = {
+      expr = splitRoot /foo/../bar;
+      expected = { root = /.; subpath = "./bar"; };
+    };
+    testSplitRootExample4 = {
+      expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
+      expected = false;
+    };
+
     # Test examples from the lib.path.subpath.isValid documentation
     testSubpathIsValidExample1 = {
       expr = subpath.isValid null;