diff options
| author | Shea Levy <shea@shealevy.com> | 2014-02-13 22:55:57 -0500 |
|---|---|---|
| committer | Shea Levy <shea@shealevy.com> | 2014-03-05 11:28:03 -0500 |
| commit | 48f41cb90e9ea79b59125c64b1e8e8f7737249fc (patch) | |
| tree | 66d45e99c51a94d90f27a5e0f7d33b135b69e478 | |
| parent | Reuse merge code to merge inner values of container option types (diff) | |
| download | nixpkgs-48f41cb90e9ea79b59125c64b1e8e8f7737249fc.tar.gz | |
Add mkMap property to the module system
This allows specifying configuration that should be merged in with some
or all elements of a container option (e.g. listOf, attrsOf) without
needing to know whether those elements are actually defined elsewhere.
For example, this will default the fsType of all filesystems except
/boot to btrfs (and leaves /boot alone):
fileSystems = mkMap (name: mkIf (name != "/boot") {
fsType = mkDefault "btrfs";
});
mkMap takes a function which takes an index of an element and returns a
value to be merged in with the other definitions for that element. For
sets, the index is just the attribute name. For lists, the mkMap
function is passed two arguments: The index of the relevant definition
in the list of all definitions for that option, and the index of the
element within that definition.
| -rw-r--r-- | lib/modules.nix | 22 | ||||
| -rw-r--r-- | lib/types.nix | 35 |
2 files changed, 49 insertions, 8 deletions
diff --git a/lib/modules.nix b/lib/modules.nix index fc25582fa059..d48ffe255120 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -165,6 +165,9 @@ rec { value = (opt.apply or id) merged; in opt // { value = addErrorContext "while evaluating the option `${showOption loc}':" value; + # Note that definitions may contain undischarged mkMap properties, + # as mkMap can only be discharged in the merge function of the + # relevant (mappable) type. definitions = map (def: def.value) defsFinal; isDefined = defsFinal != []; files = map (def: def.file) defsFinal; @@ -179,9 +182,12 @@ rec { # Merge everything, but check that types match first mergedValue = fold (def: res: - 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; + if def.value._type or "" == "map" + then if type.mappable then res + else throw "Option value `${showOption loc}' in `${def.file}' is a mkMap but ${type.name} is not mappable" + else 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; }; /* Given a config set, expand mkMerge properties, and push down the @@ -307,6 +313,16 @@ rec { mkFixStrictness = id; # obsolete, no-op + # Map a function, which takes an index ((defnNum, index) for lists, attrname + # for sets, etc) and returns a value to be merged with the other values + # defined for that index, over a collection. This can for example be used to + # apply some config to *every* submodule in an attrsOf submodule without + # needing to know which attrs are actually defined elsewhere + mkMap = f: + { _type = "map"; + inherit f; + }; + # FIXME: Add mkOrder back in. It's not currently used anywhere in # NixOS, but it should be useful. diff --git a/lib/types.nix b/lib/types.nix index 55765750af1d..b6ec854399b8 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -37,9 +37,11 @@ in rec { , # Return a flat list of sub-options. Used to generate # documentation. getSubOptions ? prefix: {} + , # A mappable type can handle mkMap defs when merging + mappable ? false }: { _type = "option-type"; - inherit name check merge getSubOptions; + inherit name check merge getSubOptions mappable; }; @@ -110,21 +112,42 @@ in rec { name = "list of ${elemType.name}s"; check = isList; merge = loc: defs: - concatLists (imap (n: def: imap (m: def': + let + nonMaps = filter (d: d.value._type or "" != "map") defs; + maps = filter (d: d.value._type or "" == "map") defs; + mapResults = fold (m: imap (x: l: imap (y: l: l ++ [ { + inherit (m) file; + value = m.value.f x y; + } ]) l)) + (map (d: map (x: []) d.value) nonMaps) maps; + in concatLists (imap (n: def: imap (m: def': innerMerge (loc ++ ["[${toString n}-${toString m}]"]) elemType - [{ inherit (def) file; value = def'; }]) def.value) defs); + ([{ inherit (def) file; value = def'; }] ++ elemAt (elemAt mapResults (n - 1)) (m - 1)) + ) def.value) nonMaps); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); + mappable = true; }; attrsOf = elemType: mkOptionType { name = "attribute set of ${elemType.name}s"; check = isAttrs; merge = loc: defs: - zipAttrsWith (name: innerMerge (loc ++ [name]) elemType) + let + nonMaps = filter (d: d.value._type or "" != "map") defs; + maps = filter (d: d.value._type or "" == "map") defs; + names = concatMap (d: attrNames d.value) nonMaps; + mapResults = listToAttrs (map (name: { + inherit name; + value = map (m: { inherit (m) file; value = m.value.f name; }) maps; + }) names); + in zipAttrsWith (name: defs: + innerMerge (loc ++ [name]) elemType (defs ++ getAttr name mapResults) + ) # Push down position info. (map (def: listToAttrs (mapAttrsToList (n: def': - { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs); + { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) nonMaps); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]); + mappable = true; }; # List or attribute set of ... @@ -148,6 +171,8 @@ in rec { check = x: isList x || isAttrs x; merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]); + # maps over the post-convertifList defs + mappable = true; }; uniq = elemType: mkOptionType { |
