Skip to content

Commit

Permalink
feat(std): add merge function
Browse files Browse the repository at this point in the history
Thanks to upstream nixpkgs, this is just a shorter name for their
`recursiveUpdate` lib function. Important and common enough to warrant
inclusion, since there is no other "out of the box" method to merge
sets recursively without destructive operations in pure Nix.
  • Loading branch information
nrdxp committed Aug 5, 2024
1 parent 2354a20 commit fda34a5
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
47 changes: 47 additions & 0 deletions std/set/merge.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
A recursive variant of the update operator ‘//’. The recursion
stops when one of the attribute values is not an attribute set,
in which case the right hand side value takes precedence over the
left hand side value.
# Inputs
`lhs`
: Left attribute set of the merge.
`rhs`
: Right attribute set of the merge.
# Type
```
merge :: AttrSet -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.attrsets.merge` usage example
```nix
merge {
boot.loader.grub.enable = true;
boot.loader.grub.device = "/dev/hda";
} {
boot.loader.grub.device = "";
}
returns: {
boot.loader.grub.enable = true;
boot.loader.grub.device = "";
}
```
:::
*/
lhs: rhs:
mod.mergeUntil (
path: lhs: rhs:
!(std.isAttrs lhs && std.isAttrs rhs)
) lhs rhs
75 changes: 75 additions & 0 deletions std/set/mergeUntil.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
Does the same as the update operator '//' except that attributes are
merged until the given predicate is verified. The predicate should
accept 3 arguments which are the path to reach the attribute, a part of
the first attribute set and a part of the second attribute set. When
the predicate is satisfied, the value of the first attribute set is
replaced by the value of the second attribute set.
# Inputs
`pred`
: Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments.
`lhs`
: Left attribute set of the merge.
`rhs`
: Right attribute set of the merge.
# Type
```
mergeUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.attrsets.mergeUntil` usage example
```nix
mergeUntil (path: l: r: path == ["foo"]) {
# first attribute set
foo.bar = 1;
foo.baz = 2;
bar = 3;
} {
#second attribute set
foo.bar = 1;
foo.quz = 2;
baz = 4;
}
=> {
foo.bar = 1; # 'foo.*' from the second set
foo.quz = 2; #
bar = 3; # 'bar' from the first set
baz = 4; # 'baz' from the second set
}
```
:::
*/

pred: lhs: rhs:
let
f =
attrPath:
std.zipAttrsWith (
n: values:
let
here = attrPath ++ [ n ];
in
if std.length values == 1 || pred here (std.elemAt values 1) (std.head values) then
std.head values
else
f here values
);
in
f [ ] [
rhs
lhs
]
1 change: 1 addition & 0 deletions std/set/mod.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
Cond = mod.cond;
FilterMap = mod.filterMap;
Merge = mod.merge;
}

0 comments on commit fda34a5

Please sign in to comment.