Merge pull request #208887 from tweag/lib.path.append
lib.path.append: init
This commit is contained in:
@@ -4,6 +4,7 @@ let
|
|||||||
|
|
||||||
inherit (builtins)
|
inherit (builtins)
|
||||||
isString
|
isString
|
||||||
|
isPath
|
||||||
split
|
split
|
||||||
match
|
match
|
||||||
;
|
;
|
||||||
@@ -25,6 +26,10 @@ let
|
|||||||
assertMsg
|
assertMsg
|
||||||
;
|
;
|
||||||
|
|
||||||
|
inherit (lib.path.subpath)
|
||||||
|
isValid
|
||||||
|
;
|
||||||
|
|
||||||
# Return the reason why a subpath is invalid, or `null` if it's valid
|
# Return the reason why a subpath is invalid, or `null` if it's valid
|
||||||
subpathInvalidReason = value:
|
subpathInvalidReason = value:
|
||||||
if ! isString value then
|
if ! isString value then
|
||||||
@@ -94,6 +99,52 @@ let
|
|||||||
|
|
||||||
in /* No rec! Add dependencies on this file at the top. */ {
|
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.
|
/* 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/"
|
subpath.isValid "./foo//bar/"
|
||||||
=> true
|
=> true
|
||||||
*/
|
*/
|
||||||
subpath.isValid = value:
|
subpath.isValid =
|
||||||
|
# The value to check
|
||||||
|
value:
|
||||||
subpathInvalidReason value == null;
|
subpathInvalidReason value == null;
|
||||||
|
|
||||||
|
|
||||||
@@ -150,11 +203,11 @@ in /* No rec! Add dependencies on this file at the top. */ {
|
|||||||
|
|
||||||
Laws:
|
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
|
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})
|
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"
|
subpath.normalise "/foo"
|
||||||
=> <error>
|
=> <error>
|
||||||
*/
|
*/
|
||||||
subpath.normalise = path:
|
subpath.normalise =
|
||||||
assert assertMsg (subpathInvalidReason path == null)
|
# The subpath string to normalise
|
||||||
"lib.path.subpath.normalise: Argument is not a valid subpath string: ${subpathInvalidReason path}";
|
subpath:
|
||||||
joinRelPath (splitRelPath path);
|
assert assertMsg (isValid subpath) ''
|
||||||
|
lib.path.subpath.normalise: Argument is not a valid subpath string:
|
||||||
|
${subpathInvalidReason subpath}'';
|
||||||
|
joinRelPath (splitRelPath subpath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,44 @@
|
|||||||
{ libpath }:
|
{ libpath }:
|
||||||
let
|
let
|
||||||
lib = import libpath;
|
lib = import libpath;
|
||||||
inherit (lib.path) subpath;
|
inherit (lib.path) append subpath;
|
||||||
|
|
||||||
cases = lib.runTests {
|
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 = {
|
testSubpathIsValidExample1 = {
|
||||||
expr = subpath.isValid null;
|
expr = subpath.isValid null;
|
||||||
expected = false;
|
expected = false;
|
||||||
@@ -30,6 +65,7 @@ let
|
|||||||
expr = subpath.isValid "./foo//bar/";
|
expr = subpath.isValid "./foo//bar/";
|
||||||
expected = true;
|
expected = true;
|
||||||
};
|
};
|
||||||
|
# Some extra tests
|
||||||
testSubpathIsValidTwoDotsEnd = {
|
testSubpathIsValidTwoDotsEnd = {
|
||||||
expr = subpath.isValid "foo/..";
|
expr = subpath.isValid "foo/..";
|
||||||
expected = false;
|
expected = false;
|
||||||
@@ -71,6 +107,7 @@ let
|
|||||||
expected = true;
|
expected = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Test examples from the lib.path.subpath.normalise documentation
|
||||||
testSubpathNormaliseExample1 = {
|
testSubpathNormaliseExample1 = {
|
||||||
expr = subpath.normalise "foo//bar";
|
expr = subpath.normalise "foo//bar";
|
||||||
expected = "./foo/bar";
|
expected = "./foo/bar";
|
||||||
@@ -107,6 +144,7 @@ let
|
|||||||
expr = (builtins.tryEval (subpath.normalise "/foo")).success;
|
expr = (builtins.tryEval (subpath.normalise "/foo")).success;
|
||||||
expected = false;
|
expected = false;
|
||||||
};
|
};
|
||||||
|
# Some extra tests
|
||||||
testSubpathNormaliseIsValidDots = {
|
testSubpathNormaliseIsValidDots = {
|
||||||
expr = subpath.normalise "./foo/.bar/.../baz...qux";
|
expr = subpath.normalise "./foo/.bar/.../baz...qux";
|
||||||
expected = "./foo/.bar/.../baz...qux";
|
expected = "./foo/.bar/.../baz...qux";
|
||||||
|
|||||||
Reference in New Issue
Block a user