Share common code for merging option definitions.

This move idioms which were used in `evalOptionValue` and in the `merge`
functions of `listOf` and `attrsOf` types, such that we can use a names such
as `isDefined` and `optionalValue` instead or repeating identical
comparisons of `defsFinal == []`.

Changed files
+21 -19
lib
+11 -7
lib/modules.nix
···
# Add in the default value for this option, if any.
defs' = (optional (opt ? default)
{ file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs;
+
# Handle properties, check types, and merge everything together
-
inherit (mergeDefinitions loc opt.type defs') defsFinal mergedValue;
+
inherit (mergeDefinitions loc opt.type defs') isDefined defsFinal mergedValue;
files = map (def: def.file) defsFinal;
merged =
-
if defsFinal == [] then
-
throw "The option `${showOption loc}' is used but not defined."
-
else
-
mergedValue;
+
if isDefined then mergedValue
+
else throw "The option `${showOption loc}' is used but not defined.";
+
# Finally, apply the ‘apply’ function to the merged
# value. This allows options to yield a value computed
# from the definitions.
···
in opt //
{ value = addErrorContext "while evaluating the option `${showOption loc}':" value;
definitions = map (def: def.value) defsFinal;
-
isDefined = defsFinal != [];
-
inherit files;
+
inherit isDefined files;
};
# Merge definitions of a value of a given type
···
if type.check def.value then res
else throw "The option value `${showOption loc}' in `${def.file}' is not a ${type.name}.")
(type.merge loc defsFinal) defsFinal;
+
+
isDefined = defsFinal != [];
+
optionalValue =
+
if isDefined then { value = mergedValue; }
+
else {};
};
/* Given a config set, expand mkMerge properties, and push down the
+10 -12
lib/types.nix
···
with import ./options.nix;
with import ./trivial.nix;
with import ./strings.nix;
+
with {inherit (import ./modules.nix) mergeDefinitions; };
-
let
-
inherit (import ./modules.nix) mergeDefinitions;
-
in rec {
+
rec {
isType = type: x: (x._type or "") == type;
···
check = isList;
merge = loc: defs:
map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def: imap (m: def':
-
let
-
inherit (mergeDefinitions (loc ++ ["[definition ${toString n}-entry ${toString m}]"])
-
elemType [{ inherit (def) file; value = def'; }]
-
) defsFinal mergedValue;
-
in if defsFinal == [] then {} else { value = mergedValue; }) def.value) defs)));
+
(mergeDefinitions
+
(loc ++ ["[definition ${toString n}-entry ${toString m}]"])
+
elemType
+
[{ inherit (def) file; value = def'; }]
+
).optionalValue
+
) def.value) defs)));
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
···
check = isAttrs;
merge = loc: defs:
mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
-
let
-
inherit (mergeDefinitions (loc ++ [name]) elemType defs)
-
defsFinal mergedValue;
-
in if defsFinal == [] then {} else { value = mergedValue; })
+
(mergeDefinitions (loc ++ [name]) elemType defs).optionalValue
+
)
# Push down position info.
(map (def: listToAttrs (mapAttrsToList (n: def':
{ name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs)));