82 lines
1.9 KiB
Nix
82 lines
1.9 KiB
Nix
{nib, ...}:
|
|
with builtins;
|
|
with nib.types; rec {
|
|
cmpStructErr' = errBadKeys: errBadValues: path: S: T:
|
|
if isAttrs S && isAttrs T
|
|
then let
|
|
keysS = attrNames S;
|
|
keysT = 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
|
|
(typeOf S == 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;
|
|
});
|
|
|
|
# check is a function taking two structs
|
|
# and returning a result monad.
|
|
mergeStruct' = check: template: S: let
|
|
R = check template S;
|
|
in
|
|
errOr ({...}:
|
|
Ok (
|
|
mapAttrsRecursive (
|
|
path: value: let
|
|
valueS = attrValueAt S path;
|
|
in
|
|
if valueS != null
|
|
then valueS
|
|
else value
|
|
)
|
|
template
|
|
))
|
|
R;
|
|
|
|
# mergeStruct ensures no properties are evaluated (entirely lazy)
|
|
mergeStruct = mergeStruct' (_: _: Ok');
|
|
|
|
# mergeTypedPartialStruct must evaluate properties (not lazy)
|
|
# for lazy evaluation use mergeStruct instead!
|
|
mergeTypedPartialStruct = mergeStruct' cmpTypedPartialStruct;
|
|
}
|