Loading lib/path/default.nix +63 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ let inherit (builtins) isString isPath split match ; Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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; Loading @@ -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}) Loading Loading @@ -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); } lib/path/tests/unit.nix +39 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -30,6 +65,7 @@ let expr = subpath.isValid "./foo//bar/"; expected = true; }; # Some extra tests testSubpathIsValidTwoDotsEnd = { expr = subpath.isValid "foo/.."; expected = false; Loading Loading @@ -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"; Loading Loading @@ -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"; Loading Loading
lib/path/default.nix +63 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ let inherit (builtins) isString isPath split match ; Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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; Loading @@ -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}) Loading Loading @@ -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); }
lib/path/tests/unit.nix +39 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -30,6 +65,7 @@ let expr = subpath.isValid "./foo//bar/"; expected = true; }; # Some extra tests testSubpathIsValidTwoDotsEnd = { expr = subpath.isValid "foo/.."; expected = false; Loading Loading @@ -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"; Loading Loading @@ -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"; Loading