111 lines
2.6 KiB
Nix
111 lines
2.6 KiB
Nix
{nib, ...}: let
|
|
inherit
|
|
(nib.types)
|
|
Err
|
|
Ok'
|
|
firstErr
|
|
unwrapSome
|
|
isTerminal
|
|
unwrapTerminal
|
|
;
|
|
|
|
inherit
|
|
(nib.std)
|
|
attrValueAt
|
|
;
|
|
in rec {
|
|
cmpStructErr' = errBadKeys: errBadValues: path: S: T:
|
|
if builtins.isAttrs S && builtins.isAttrs T
|
|
then let
|
|
keysS = builtins.attrNames S;
|
|
keysT = builtins.attrNames T;
|
|
in
|
|
# ensure all key names match, then recurse
|
|
if !(keysS == keysT)
|
|
then errBadKeys path keysS keysT
|
|
else
|
|
(firstErr
|
|
(map
|
|
(k: cmpStructErr' errBadKeys errBadValues (path ++ [k]) (keysS.${k}) (keysT.${k}))
|
|
keysS))
|
|
else
|
|
# terminating leaf in recursion tree reached
|
|
# ensure values' types match
|
|
(builtins.typeOf S == builtins.typeOf T)
|
|
|| errBadValues path S T;
|
|
|
|
cmpStructErr = errBadKeys: errBadValues: cmpStructErr' errBadKeys errBadValues [];
|
|
|
|
cmpStruct =
|
|
cmpStructErr
|
|
(path: _: _:
|
|
Err {
|
|
reason = "keys";
|
|
inherit path;
|
|
})
|
|
(_: _: _: Ok');
|
|
|
|
cmpTypedStruct =
|
|
cmpStructErr
|
|
(path: _: _:
|
|
Err {
|
|
reason = "keys";
|
|
inherit path;
|
|
})
|
|
(path: _: _:
|
|
Err {
|
|
reason = "values";
|
|
inherit path;
|
|
});
|
|
|
|
cmpTypedPartialStruct =
|
|
cmpStructErr
|
|
(_: _: _: Ok')
|
|
(path: _: _:
|
|
Err {
|
|
reason = "values";
|
|
inherit path;
|
|
});
|
|
|
|
# Alternative to mapAttrsRecursiveCond
|
|
# Allows mapping directly from a child path
|
|
recmapCondFrom = path: cond: f: T: let
|
|
delegate = path': recmapCondFrom path' cond f;
|
|
in
|
|
if builtins.isAttrs T && cond path T
|
|
then builtins.mapAttrs (attr: leaf: delegate (path ++ [attr]) leaf) T
|
|
# else if builtins.isList T
|
|
# then map (leaf: delegate leaf)
|
|
else f path T;
|
|
|
|
recmapCond = recmapCondFrom [];
|
|
|
|
# Alternative to mapAttrsRecursive
|
|
# NOTE: refuses to go beyond Terminal types
|
|
recmap = recmapCond (_: leaf: !(isTerminal leaf));
|
|
|
|
overrideStructCond = cond: f: S: ext:
|
|
recmapCond
|
|
cond
|
|
(path: leaf:
|
|
attrValueAt path ext
|
|
|> unwrapSome (_: f leaf))
|
|
S;
|
|
|
|
# overrideStruct ensures no properties are evaluated (entirely lazy)
|
|
# TODO: should this be called "overlayStructs" or something? (its not exactly a override...)
|
|
# NOTE: respects Terminal types
|
|
overrideStruct =
|
|
overrideStructCond
|
|
(_: leaf: !(isTerminal leaf))
|
|
(leaf:
|
|
if isTerminal leaf
|
|
then unwrapTerminal leaf
|
|
else leaf);
|
|
|
|
# # overrideTypedPartialStruct must evaluate properties (not lazy)
|
|
# # for lazy evaluation use overrideStruct instead!
|
|
# overrideTypedPartialStruct = overrideStructs' cmpTypedPartialStruct;
|
|
|
|
overrideAttrs = A: B: A // B;
|
|
}
|