Merge pull request #237610 from tweag/lib.path.hasPrefix
`lib.path.hasPrefix`: init
This commit is contained in:
commit
43be38bd78
@ -7,6 +7,7 @@ let
|
||||
isPath
|
||||
split
|
||||
match
|
||||
typeOf
|
||||
;
|
||||
|
||||
inherit (lib.lists)
|
||||
@ -18,6 +19,7 @@ let
|
||||
all
|
||||
concatMap
|
||||
foldl'
|
||||
take
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
@ -100,6 +102,22 @@ let
|
||||
# An empty string is not a valid relative path, so we need to return a `.` when we have no components
|
||||
(if components == [] then "." else concatStringsSep "/" components);
|
||||
|
||||
# Type: Path -> { root :: Path, components :: [ String ] }
|
||||
#
|
||||
# Deconstruct a path value type into:
|
||||
# - root: The filesystem root of the path, generally `/`
|
||||
# - components: All the path's components
|
||||
#
|
||||
# This is similar to `splitString "/" (toString path)` but safer
|
||||
# because it can distinguish different filesystem roots
|
||||
deconstructPath =
|
||||
let
|
||||
recurse = components: base:
|
||||
# If the parent of a path is the path itself, then it's a filesystem root
|
||||
if base == dirOf base then { root = base; inherit components; }
|
||||
else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
|
||||
in recurse [];
|
||||
|
||||
in /* No rec! Add dependencies on this file at the top. */ {
|
||||
|
||||
/* Append a subpath string to a path.
|
||||
@ -108,6 +126,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
|
||||
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`).
|
||||
|
||||
Laws:
|
||||
|
||||
- Not influenced by subpath normalisation
|
||||
|
||||
append p s == append p (subpath.normalise s)
|
||||
|
||||
Type:
|
||||
append :: Path -> String -> Path
|
||||
|
||||
@ -149,6 +173,51 @@ in /* No rec! Add dependencies on this file at the top. */ {
|
||||
${subpathInvalidReason subpath}'';
|
||||
path + ("/" + subpath);
|
||||
|
||||
/*
|
||||
Whether the first path is a component-wise prefix of the second path.
|
||||
|
||||
Laws:
|
||||
|
||||
- `hasPrefix p q` is only true if `q == append p s` for some subpath `s`.
|
||||
|
||||
- `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values
|
||||
|
||||
Type:
|
||||
hasPrefix :: Path -> Path -> Bool
|
||||
|
||||
Example:
|
||||
hasPrefix /foo /foo/bar
|
||||
=> true
|
||||
hasPrefix /foo /foo
|
||||
=> true
|
||||
hasPrefix /foo/bar /foo
|
||||
=> false
|
||||
hasPrefix /. /foo
|
||||
=> true
|
||||
*/
|
||||
hasPrefix =
|
||||
path1:
|
||||
assert assertMsg
|
||||
(isPath path1)
|
||||
"lib.path.hasPrefix: First argument is of type ${typeOf path1}, but a path was expected";
|
||||
let
|
||||
path1Deconstructed = deconstructPath path1;
|
||||
in
|
||||
path2:
|
||||
assert assertMsg
|
||||
(isPath path2)
|
||||
"lib.path.hasPrefix: Second argument is of type ${typeOf path2}, but a path was expected";
|
||||
let
|
||||
path2Deconstructed = deconstructPath path2;
|
||||
in
|
||||
assert assertMsg
|
||||
(path1Deconstructed.root == path2Deconstructed.root) ''
|
||||
lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
|
||||
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
|
||||
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
|
||||
take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
|
||||
|
||||
|
||||
/* Whether a value is a valid subpath string.
|
||||
|
||||
- The value is a string
|
||||
|
@ -3,7 +3,7 @@
|
||||
{ libpath }:
|
||||
let
|
||||
lib = import libpath;
|
||||
inherit (lib.path) append subpath;
|
||||
inherit (lib.path) hasPrefix append subpath;
|
||||
|
||||
cases = lib.runTests {
|
||||
# Test examples from the lib.path.append documentation
|
||||
@ -40,6 +40,23 @@ let
|
||||
expected = false;
|
||||
};
|
||||
|
||||
testHasPrefixExample1 = {
|
||||
expr = hasPrefix /foo /foo/bar;
|
||||
expected = true;
|
||||
};
|
||||
testHasPrefixExample2 = {
|
||||
expr = hasPrefix /foo /foo;
|
||||
expected = true;
|
||||
};
|
||||
testHasPrefixExample3 = {
|
||||
expr = hasPrefix /foo/bar /foo;
|
||||
expected = false;
|
||||
};
|
||||
testHasPrefixExample4 = {
|
||||
expr = hasPrefix /. /foo;
|
||||
expected = true;
|
||||
};
|
||||
|
||||
# Test examples from the lib.path.subpath.isValid documentation
|
||||
testSubpathIsValidExample1 = {
|
||||
expr = subpath.isValid null;
|
||||
|
@ -264,7 +264,8 @@ rec {
|
||||
lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported.
|
||||
There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
|
||||
This function also copies the path to the Nix store, which may not be what you want.
|
||||
This behavior is deprecated and will throw an error in the future.''
|
||||
This behavior is deprecated and will throw an error in the future.
|
||||
You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.''
|
||||
(substring 0 (stringLength pref) str == pref);
|
||||
|
||||
/* Determine whether a string has given suffix.
|
||||
|
Loading…
Reference in New Issue
Block a user