summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2014-02-13 22:55:57 -0500
committerShea Levy <shea@shealevy.com>2014-03-05 11:28:03 -0500
commit48f41cb90e9ea79b59125c64b1e8e8f7737249fc (patch)
tree66d45e99c51a94d90f27a5e0f7d33b135b69e478
parentReuse merge code to merge inner values of container option types (diff)
downloadnixpkgs-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.nix22
-rw-r--r--lib/types.nix35
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 {