Merge pull request #208887 from tweag/lib.path.append

lib.path.append: init
This commit is contained in:
Silvan Mosberger 2023-02-07 17:27:37 +01:00 committed by GitHub
commit a770c0393c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 8 deletions

View file

@ -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);
}

View file

@ -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";