lib.path.hasPrefix: init

This commit is contained in:
Silvan Mosberger 2023-04-05 20:31:50 +02:00
parent 866f75e5b9
commit 592213ad3f
3 changed files with 83 additions and 2 deletions

View File

@ -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.
@ -155,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

View File

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

View File

@ -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.