Merge pull request #269552 from adisbladis/lib-matchattrs-list-allocs

lib.attrsets.matchAttrs: Avoid some list allocations when walking structure
This commit is contained in:
Robert Hensing 2023-11-27 14:44:37 +01:00 committed by GitHub
commit 51357572f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 9 deletions

View file

@ -897,7 +897,10 @@ rec {
recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs; recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs;
/* Returns true if the pattern is contained in the set. False otherwise. /*
Recurse into every attribute set of the first argument and check that:
- Each attribute path also exists in the second argument.
- If the attribute's value is not a nested attribute set, it must have the same value in the right argument.
Example: Example:
matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
@ -909,16 +912,24 @@ rec {
matchAttrs = matchAttrs =
# Attribute set structure to match # Attribute set structure to match
pattern: pattern:
# Attribute set to find patterns in # Attribute set to check
attrs: attrs:
assert isAttrs pattern; assert isAttrs pattern;
all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: all
let pat = head values; val = elemAt values 1; in ( # Compare equality between `pattern` & `attrs`.
if length values == 1 then false attr:
else if isAttrs pat then isAttrs val && matchAttrs pat val # Missing attr, not equal.
else pat == val attrs ? ${attr} && (
) [pattern attrs])); let
lhs = pattern.${attr};
rhs = attrs.${attr};
in
# If attrset check recursively
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs
else lhs == rhs
)
)
(attrNames pattern);
/* Override only the attributes that are already present in the old set /* Override only the attributes that are already present in the old set
useful for deep-overriding. useful for deep-overriding.

View file

@ -831,6 +831,26 @@ runTests {
}; };
}; };
testMatchAttrsMatchingExact = {
expr = matchAttrs { cpu = { bits = 64; }; } { cpu = { bits = 64; }; };
expected = true;
};
testMatchAttrsMismatch = {
expr = matchAttrs { cpu = { bits = 128; }; } { cpu = { bits = 64; }; };
expected = false;
};
testMatchAttrsMatchingImplicit = {
expr = matchAttrs { cpu = { }; } { cpu = { bits = 64; }; };
expected = true;
};
testMatchAttrsMissingAttrs = {
expr = matchAttrs { cpu = {}; } { };
expected = false;
};
testOverrideExistingEmpty = { testOverrideExistingEmpty = {
expr = overrideExisting {} { a = 1; }; expr = overrideExisting {} { a = 1; };
expected = {}; expected = {};