lib.attrsets.longestValidPathPrefix: init
Allows finding the most specific path that exists. This is useful for error messages relating to attribute paths.
This commit is contained in:
parent
50793752a7
commit
72bd4bbb58
@ -80,6 +80,71 @@ rec {
|
|||||||
in
|
in
|
||||||
hasAttrByPath' 0 e;
|
hasAttrByPath' 0 e;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets.
|
||||||
|
|
||||||
|
Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition,
|
||||||
|
although this will evaluate the predicate function on sibling attributes as well.
|
||||||
|
|
||||||
|
Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does.
|
||||||
|
|
||||||
|
**Laws**:
|
||||||
|
1. ```nix
|
||||||
|
attrsets.longestValidPathPrefix [] x == []
|
||||||
|
```
|
||||||
|
|
||||||
|
2. ```nix
|
||||||
|
hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
x = { a = { b = 3; }; }
|
||||||
|
attrsets.longestValidPathPrefix ["a" "b" "c"] x
|
||||||
|
=> ["a" "b"]
|
||||||
|
attrsets.longestValidPathPrefix ["a"] x
|
||||||
|
=> ["a"]
|
||||||
|
attrsets.longestValidPathPrefix ["z" "z"] x
|
||||||
|
=> []
|
||||||
|
attrsets.longestValidPathPrefix ["z" "z"] (throw "no need")
|
||||||
|
=> []
|
||||||
|
|
||||||
|
Type:
|
||||||
|
attrsets.longestValidPathPrefix :: [String] -> Value -> [String]
|
||||||
|
*/
|
||||||
|
longestValidPathPrefix =
|
||||||
|
# A list of strings representing the longest possible path that may be returned.
|
||||||
|
attrPath:
|
||||||
|
# The nested attribute set to check.
|
||||||
|
v:
|
||||||
|
let
|
||||||
|
lenAttrPath = length attrPath;
|
||||||
|
getPrefixForSetAtIndex =
|
||||||
|
# The nested attribute set to check, if it is an attribute set, which
|
||||||
|
# is not a given.
|
||||||
|
remainingSet:
|
||||||
|
# The index of the attribute we're about to check, as well as
|
||||||
|
# the length of the prefix we've already checked.
|
||||||
|
remainingPathIndex:
|
||||||
|
|
||||||
|
if remainingPathIndex == lenAttrPath then
|
||||||
|
# All previously checked attributes exist, and no attr names left,
|
||||||
|
# so we return the whole path.
|
||||||
|
attrPath
|
||||||
|
else
|
||||||
|
let
|
||||||
|
attr = elemAt attrPath remainingPathIndex;
|
||||||
|
in
|
||||||
|
if remainingSet ? ${attr} then
|
||||||
|
getPrefixForSetAtIndex
|
||||||
|
remainingSet.${attr} # advance from the set to the attribute value
|
||||||
|
(remainingPathIndex + 1) # advance the path
|
||||||
|
else
|
||||||
|
# The attribute doesn't exist, so we return the prefix up to the
|
||||||
|
# previously checked length.
|
||||||
|
take remainingPathIndex attrPath;
|
||||||
|
in
|
||||||
|
getPrefixForSetAtIndex v 0;
|
||||||
|
|
||||||
/* Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
/* Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -697,6 +697,46 @@ runTests {
|
|||||||
expected = false;
|
expected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_empty_empty = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ ] { };
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_empty_nonStrict = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ ] (throw "do not use");
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_zero = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "a" (throw "do not use") ] { d = null; };
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_zero_b = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "z" "z" ] "remarkably harmonious";
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_one = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a = null; };
|
||||||
|
expected = [ "a" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_two = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b = null; };
|
||||||
|
expected = [ "a" "b" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_three = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c = null; };
|
||||||
|
expected = [ "a" "b" "c" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testLongestValidPathPrefix_three_extra = {
|
||||||
|
expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c.d = throw "nope"; };
|
||||||
|
expected = [ "a" "b" "c" ];
|
||||||
|
};
|
||||||
|
|
||||||
testFindFirstIndexExample1 = {
|
testFindFirstIndexExample1 = {
|
||||||
expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
|
expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
|
||||||
expected = 1;
|
expected = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user