about summary refs log tree commit diff
path: root/lib/path
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2023-02-07 17:27:37 +0100
committerGitHub <noreply@github.com>2023-02-07 17:27:37 +0100
commita770c0393cb52777794c01cdb5a412883a732c19 (patch)
treee32ede90c76f93523959dca0e6b7113ecc4e60f0 /lib/path
parentc2a128317742fa241db73d6ace30348bb45a6824 (diff)
parenteac2538707ee6edd475cb40bfa2ec3d2c05c3ac0 (diff)
downloadnixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar.gz
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar.bz2
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar.lz
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar.xz
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.tar.zst
nixlib-a770c0393cb52777794c01cdb5a412883a732c19.zip
Merge pull request #208887 from tweag/lib.path.append
lib.path.append: init
Diffstat (limited to 'lib/path')
-rw-r--r--lib/path/default.nix70
-rw-r--r--lib/path/tests/unit.nix40
2 files changed, 102 insertions, 8 deletions
diff --git a/lib/path/default.nix b/lib/path/default.nix
index 96a9244407bf..075e2fc0d137 100644
--- a/lib/path/default.nix
+++ b/lib/path/default.nix
@@ -4,6 +4,7 @@ let
 
   inherit (builtins)
     isString
+    isPath
     split
     match
     ;
@@ -25,6 +26,10 @@ let
     assertMsg
     ;
 
+  inherit (lib.path.subpath)
+    isValid
+    ;
+
   # Return the reason why a subpath is invalid, or `null` if it's valid
   subpathInvalidReason = value:
     if ! isString value then
@@ -94,6 +99,52 @@ let
 
 in /* No rec! Add dependencies on this file at the top. */ {
 
+  /* Append a subpath string to a path.
+
+    Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results.
+    More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"),
+    and that the second argument is a valid subpath string (see `lib.path.subpath.isValid`).
+
+    Type:
+      append :: Path -> String -> Path
+
+    Example:
+      append /foo "bar/baz"
+      => /foo/bar/baz
+
+      # subpaths don't need to be normalised
+      append /foo "./bar//baz/./"
+      => /foo/bar/baz
+
+      # can append to root directory
+      append /. "foo/bar"
+      => /foo/bar
+
+      # first argument needs to be a path value type
+      append "/foo" "bar"
+      => <error>
+
+      # second argument needs to be a valid subpath string
+      append /foo /bar
+      => <error>
+      append /foo ""
+      => <error>
+      append /foo "/bar"
+      => <error>
+      append /foo "../bar"
+      => <error>
+  */
+  append =
+    # The absolute path to append to
+    path:
+    # The subpath string to append
+    subpath:
+    assert assertMsg (isPath path) ''
+      lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected'';
+    assert assertMsg (isValid subpath) ''
+      lib.path.append: Second argument is not a valid subpath string:
+          ${subpathInvalidReason subpath}'';
+    path + ("/" + subpath);
 
   /* Whether a value is a valid subpath string.
 
@@ -133,7 +184,9 @@ in /* No rec! Add dependencies on this file at the top. */ {
     subpath.isValid "./foo//bar/"
     => true
   */
-  subpath.isValid = value:
+  subpath.isValid =
+    # The value to check
+    value:
     subpathInvalidReason value == null;
 
 
@@ -150,11 +203,11 @@ in /* No rec! Add dependencies on this file at the top. */ {
 
   Laws:
 
-  - (Idempotency) Normalising multiple times gives the same result:
+  - Idempotency - normalising multiple times gives the same result:
 
         subpath.normalise (subpath.normalise p) == subpath.normalise p
 
-  - (Uniqueness) There's only a single normalisation for the paths that lead to the same file system node:
+  - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node:
 
         subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q})
 
@@ -210,9 +263,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
     subpath.normalise "/foo"
     => <error>
   */
-  subpath.normalise = path:
-    assert assertMsg (subpathInvalidReason path == null)
-      "lib.path.subpath.normalise: Argument is not a valid subpath string: ${subpathInvalidReason path}";
-    joinRelPath (splitRelPath path);
+  subpath.normalise =
+    # The subpath string to normalise
+    subpath:
+    assert assertMsg (isValid subpath) ''
+      lib.path.subpath.normalise: Argument is not a valid subpath string:
+          ${subpathInvalidReason subpath}'';
+    joinRelPath (splitRelPath subpath);
 
 }
diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix
index eccf3b7b1c33..a1a45173a909 100644
--- a/lib/path/tests/unit.nix
+++ b/lib/path/tests/unit.nix
@@ -3,9 +3,44 @@
 { libpath }:
 let
   lib = import libpath;
-  inherit (lib.path) subpath;
+  inherit (lib.path) append subpath;
 
   cases = lib.runTests {
+    # Test examples from the lib.path.append documentation
+    testAppendExample1 = {
+      expr = append /foo "bar/baz";
+      expected = /foo/bar/baz;
+    };
+    testAppendExample2 = {
+      expr = append /foo "./bar//baz/./";
+      expected = /foo/bar/baz;
+    };
+    testAppendExample3 = {
+      expr = append /. "foo/bar";
+      expected = /foo/bar;
+    };
+    testAppendExample4 = {
+      expr = (builtins.tryEval (append "/foo" "bar")).success;
+      expected = false;
+    };
+    testAppendExample5 = {
+      expr = (builtins.tryEval (append /foo /bar)).success;
+      expected = false;
+    };
+    testAppendExample6 = {
+      expr = (builtins.tryEval (append /foo "")).success;
+      expected = false;
+    };
+    testAppendExample7 = {
+      expr = (builtins.tryEval (append /foo "/bar")).success;
+      expected = false;
+    };
+    testAppendExample8 = {
+      expr = (builtins.tryEval (append /foo "../bar")).success;
+      expected = false;
+    };
+
+    # Test examples from the lib.path.subpath.isValid documentation
     testSubpathIsValidExample1 = {
       expr = subpath.isValid null;
       expected = false;
@@ -30,6 +65,7 @@ let
       expr = subpath.isValid "./foo//bar/";
       expected = true;
     };
+    # Some extra tests
     testSubpathIsValidTwoDotsEnd = {
       expr = subpath.isValid "foo/..";
       expected = false;
@@ -71,6 +107,7 @@ let
       expected = true;
     };
 
+    # Test examples from the lib.path.subpath.normalise documentation
     testSubpathNormaliseExample1 = {
       expr = subpath.normalise "foo//bar";
       expected = "./foo/bar";
@@ -107,6 +144,7 @@ let
       expr = (builtins.tryEval (subpath.normalise "/foo")).success;
       expected = false;
     };
+    # Some extra tests
     testSubpathNormaliseIsValidDots = {
       expr = subpath.normalise "./foo/.bar/.../baz...qux";
       expected = "./foo/.bar/.../baz...qux";