diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 52fbab0aceb..e210303e0e0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3505,6 +3505,49 @@ static RegisterPrimOp primop_mapAttrs({ .fun = prim_mapAttrs, }); +static void prim_filterAttrs(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.filterAttrs"); + + if (args[1]->attrs()->empty()) { + v = *args[1]; + return; + } + + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterAttrs"); + + auto attrs = state.buildBindings(args[1]->attrs()->size()); + + for (auto & i : *args[1]->attrs()) { + Value * vName = Value::toPtr(state.symbols[i.name]); + Value * callArgs[] = {vName, i.value}; + Value res; + state.callFunction(*args[0], callArgs, res, noPos); + if (state.forceBool( + res, pos, "while evaluating the return value of the filtering function passed to builtins.filterAttrs")) + attrs.insert(i.name, i.value); + } + + v.mkAttrs(attrs.alreadySorted()); +} + +static RegisterPrimOp primop_filterAttrs({ + .name = "__filterAttrs", + .args = {"f", "attrset"}, + .doc = R"( + Return an attribute set consisting of the attributes in *attrset* for which + the function *f* returns `true`. The function *f* is called with two arguments: + the name of the attribute and the value of the attribute. For example, + + ```nix + builtins.filterAttrs (name: value: name == "foo") { foo = 1; bar = 2; } + ``` + + evaluates to `{ foo = 1; }`. + )", + .fun = prim_filterAttrs, +}); + static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value ** args, Value & v) { // we will first count how many values are present for each given key. diff --git a/tests/functional/lang/eval-okay-filterattrs-names.exp b/tests/functional/lang/eval-okay-filterattrs-names.exp new file mode 100644 index 00000000000..3f07d6e1a02 --- /dev/null +++ b/tests/functional/lang/eval-okay-filterattrs-names.exp @@ -0,0 +1 @@ +{ a = 3; } diff --git a/tests/functional/lang/eval-okay-filterattrs-names.nix b/tests/functional/lang/eval-okay-filterattrs-names.nix new file mode 100644 index 00000000000..94108fbefda --- /dev/null +++ b/tests/functional/lang/eval-okay-filterattrs-names.nix @@ -0,0 +1,5 @@ +builtins.filterAttrs (name: value: name == "a") { + a = 3; + b = 6; + c = 10; +} diff --git a/tests/functional/lang/eval-okay-filterattrs.exp b/tests/functional/lang/eval-okay-filterattrs.exp new file mode 100644 index 00000000000..74b9825e9c4 --- /dev/null +++ b/tests/functional/lang/eval-okay-filterattrs.exp @@ -0,0 +1 @@ +{ b = 6; c = 10; } diff --git a/tests/functional/lang/eval-okay-filterattrs.nix b/tests/functional/lang/eval-okay-filterattrs.nix new file mode 100644 index 00000000000..28d37bbe784 --- /dev/null +++ b/tests/functional/lang/eval-okay-filterattrs.nix @@ -0,0 +1,5 @@ +builtins.filterAttrs (name: value: value > 5) { + a = 3; + b = 6; + c = 10; +}