nt/nt/primitives/bootstrap/prim/attrs.nix
2026-01-28 12:55:12 +10:00

92 lines
2.3 KiB
Nix

{this, ...}: let
inherit
(builtins)
attrNames
elem
elemAt
filter
hasAttr
head
length
listToAttrs
mapAttrs
partition
removeAttrs
tail
typeOf
;
inherit
(this)
flip
id
;
in rec {
enfIsAttrs = value: msg: let
got = typeOf value;
in
got == "set" || throw "${msg}: expected primitive nix type \"set\" but got \"${got}\"";
# NOTE: doesn't check if xs is type set, use enfHasAttr instead
enfHasAttrUnsafe = name: xs: msg:
hasAttr name xs || throw "${msg}: missing required attribute \"${name}\"";
# NOTE: use enfHasAttr' if you can guarantee xs is type set
enfHasAttr = name: xs: msg:
enfIsAttrs xs msg && enfHasAttrUnsafe name xs msg;
getAttrOr = name: f: xs:
if xs ? ${name}
then xs.${name}
else f xs;
getAttrDefault = name: default: getAttrOr name (_: default);
genAttrs = names: f:
names
|> map (n: nameValuePair n (f n))
|> listToAttrs;
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
elemAt list start;
in
if list == []
then
# Calling binaryMerge as below would not satisfy its invariant
{}
else binaryMerge 0 (length list);
removeAttrsRec = paths: xs: let
parts = partition (p: length p == 1) paths;
here = parts.right;
next = parts.wrong;
in
xs
|> flip removeAttrs here
|> mapAttrs (name:
if ! elem name next
then id
else
next
|> filter (x: head x == name)
|> map tail
|> removeAttrsRec);
filterAttrs = pred: xs:
attrNames xs
|> filter (name: ! pred name xs.${name})
|> removeAttrs xs;
nameValuePair = name: value: {inherit name value;};
}