130 lines
3.3 KiB
Nix
130 lines
3.3 KiB
Nix
{nib, ...}: let
|
|
foldl = nib.std.foldl;
|
|
inherit
|
|
(nib.types)
|
|
nullableToMaybe
|
|
;
|
|
in rec {
|
|
nameValuePair = name: value: {inherit name value;};
|
|
|
|
identityAttrs = value: {${value} = value;};
|
|
|
|
identityAttrsMany = values: map (v: identityAttrs v) values;
|
|
|
|
/**
|
|
Generate an attribute set by mapping a function over a list of
|
|
attribute names.
|
|
|
|
# Inputs
|
|
|
|
`names`
|
|
|
|
: Names of values in the resulting attribute set.
|
|
|
|
`f`
|
|
|
|
: A function, given the name of the attribute, returns the attribute's value.
|
|
|
|
# Type
|
|
|
|
```
|
|
genAttrs :: [ String ] -> (String -> Any) -> AttrSet
|
|
```
|
|
|
|
# Examples
|
|
:::{.example}
|
|
## `lib.attrsets.genAttrs` usage example
|
|
|
|
```nix
|
|
genAttrs [ "foo" "bar" ] (name: "x_" + name)
|
|
=> { foo = "x_foo"; bar = "x_bar"; }
|
|
```
|
|
|
|
:::
|
|
*/
|
|
genAttrs = names: f: genAttrs' names (n: nameValuePair n (f n));
|
|
|
|
/**
|
|
Like `genAttrs`, but allows the name of each attribute to be specified in addition to the value.
|
|
The applied function should return both the new name and value as a `nameValuePair`.
|
|
::: {.warning}
|
|
In case of attribute name collision the first entry determines the value,
|
|
all subsequent conflicting entries for the same name are silently ignored.
|
|
:::
|
|
|
|
# Inputs
|
|
|
|
`xs`
|
|
|
|
: A list of strings `s` used as generator.
|
|
|
|
`f`
|
|
|
|
: A function, given a string `s` from the list `xs`, returns a new `nameValuePair`.
|
|
|
|
# Type
|
|
|
|
```
|
|
genAttrs' :: [ Any ] -> (Any -> { name :: String; value :: Any; }) -> AttrSet
|
|
```
|
|
|
|
# Examples
|
|
:::{.example}
|
|
## `lib.attrsets.genAttrs'` usage example
|
|
|
|
```nix
|
|
genAttrs' [ "foo" "bar" ] (s: nameValuePair ("x_" + s) ("y_" + s))
|
|
=> { x_foo = "y_foo"; x_bar = "y_bar"; }
|
|
```
|
|
|
|
:::
|
|
*/
|
|
genAttrs' = xs: f: builtins.listToAttrs (map f xs);
|
|
|
|
mapAttrsRecursiveCond = cond: f: set: let
|
|
recurse = path:
|
|
builtins.mapAttrs (
|
|
name: value: let
|
|
next = path ++ [name];
|
|
in
|
|
if builtins.isAttrs value && cond value
|
|
then recurse next value
|
|
else f next value
|
|
);
|
|
in
|
|
recurse [] set;
|
|
|
|
mapAttrsRecursive = f: set: mapAttrsRecursiveCond (as: true) f set;
|
|
|
|
# form: attrValueAt :: list string -> set -> Maybe Any
|
|
# given path as a list of strings, return that value of an
|
|
# attribute set at that path
|
|
attrValueAt = path: xs:
|
|
foldl (left: right:
|
|
if builtins.isAttrs left && builtins.hasAttr right left
|
|
then left.${right}
|
|
else null)
|
|
xs
|
|
path
|
|
|> nullableToMaybe;
|
|
|
|
mergeAttrsList = list: let
|
|
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
|
# Type: Int -> Int -> Attrs
|
|
binaryMerge = start: end:
|
|
# assert start < end; # Invariant
|
|
if end - start >= 2
|
|
then
|
|
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
|
# The invariant is satisfied because each half will have at least 1 element
|
|
binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end
|
|
else
|
|
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
|
builtins.elemAt list start;
|
|
in
|
|
if list == []
|
|
then
|
|
# Calling binaryMerge as below would not satisfy its invariant
|
|
{}
|
|
else binaryMerge 0 (builtins.length list);
|
|
}
|