aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/lib/types.nix
diff options
context:
space:
mode:
authorKatharina Fey <kookie@spacekookie.de>2020-02-03 09:26:35 +0100
committerKatharina Fey <kookie@spacekookie.de>2020-02-03 09:26:35 +0100
commit899a451e08f7d6d2c8214d119c2a0316849a0ed4 (patch)
tree5e72a7288b7d2b33fead36fbfe91a02a48ff7fef /nixpkgs/lib/types.nix
parent5962418b6543dfb3ca34965c0fa16dd77543801b (diff)
parenta21c2fa3ea2b88e698db6fc151d9c7259ae14d96 (diff)
Merge commit 'a21c2fa3ea2b88e698db6fc151d9c7259ae14d96'
Diffstat (limited to 'nixpkgs/lib/types.nix')
-rw-r--r--nixpkgs/lib/types.nix122
1 files changed, 107 insertions, 15 deletions
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index 4872a676657..6fd6de7e1fd 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -65,6 +65,11 @@ rec {
# definition values and locations (e.g. [ { file = "/foo.nix";
# value = 1; } { file = "/bar.nix"; value = 2 } ]).
merge ? mergeDefaultOption
+ , # Whether this type has a value representing nothingness. If it does,
+ # this should be a value of the form { value = <the nothing value>; }
+ # If it doesn't, this should be {}
+ # This may be used when a value is required for `mkIf false`. This allows the extra laziness in e.g. `lazyAttrsOf`.
+ emptyValue ? {}
, # Return a flat list of sub-options. Used to generate
# documentation.
getSubOptions ? prefix: {}
@@ -88,7 +93,7 @@ rec {
functor ? defaultFunctor name
}:
{ _type = "option-type";
- inherit name check merge getSubOptions getSubModules substSubModules typeMerge functor;
+ inherit name check merge emptyValue getSubOptions getSubModules substSubModules typeMerge functor;
description = if description == null then name else description;
};
@@ -225,6 +230,7 @@ rec {
description = "attribute set";
check = isAttrs;
merge = loc: foldl' (res: def: mergeAttrs res def.value) {};
+ emptyValue = { value = {}; };
};
# derivation is a reserved keyword.
@@ -265,6 +271,7 @@ rec {
) def.value
else
throw "The option value `${showOption loc}` in `${def.file}` is not a list.") defs)));
+ emptyValue = { value = {}; };
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
@@ -273,7 +280,10 @@ rec {
nonEmptyListOf = elemType:
let list = addCheck (types.listOf elemType) (l: l != []);
- in list // { description = "non-empty " + list.description; };
+ in list // {
+ description = "non-empty " + list.description;
+ # Note: emptyValue is left as is, because another module may define an element.
+ };
attrsOf = elemType: mkOptionType rec {
name = "attrsOf";
@@ -285,12 +295,37 @@ rec {
)
# Push down position info.
(map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs)));
+ emptyValue = { value = {}; };
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
};
+ # A version of attrsOf that's lazy in its values at the expense of
+ # conditional definitions not working properly. E.g. defining a value with
+ # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with
+ # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an
+ # error that it's not defined. Use only if conditional definitions don't make sense.
+ lazyAttrsOf = elemType: mkOptionType rec {
+ name = "lazyAttrsOf";
+ description = "lazy attribute set of ${elemType.description}s";
+ check = isAttrs;
+ merge = loc: defs:
+ zipAttrsWith (name: defs:
+ let merged = mergeDefinitions (loc ++ [name]) elemType defs;
+ # mergedValue will trigger an appropriate error when accessed
+ in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue
+ )
+ # Push down position info.
+ (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
+ emptyValue = { value = {}; };
+ getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
+ getSubModules = elemType.getSubModules;
+ substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
+ functor = (defaultFunctor name) // { wrapped = elemType; };
+ };
+
# List or attribute set of ...
loaOf = elemType:
let
@@ -305,29 +340,79 @@ rec {
let
padWidth = stringLength (toString (length def.value));
unnamed = i: unnamedPrefix + fixedWidthNumber padWidth i;
+ anyString = placeholder "name";
+ nameAttrs = [
+ { path = [ "environment" "etc" ];
+ name = "target";
+ }
+ { path = [ "containers" anyString "bindMounts" ];
+ name = "mountPoint";
+ }
+ { path = [ "programs" "ssh" "knownHosts" ];
+ # hostNames is actually a list so we would need to handle it only when singleton
+ name = "hostNames";
+ }
+ { path = [ "fileSystems" ];
+ name = "mountPoint";
+ }
+ { path = [ "boot" "specialFileSystems" ];
+ name = "mountPoint";
+ }
+ { path = [ "services" "znapzend" "zetup" ];
+ name = "dataset";
+ }
+ { path = [ "services" "znapzend" "zetup" anyString "destinations" ];
+ name = "label";
+ }
+ { path = [ "services" "geoclue2" "appConfig" ];
+ name = "desktopID";
+ }
+ ];
+ matched = let
+ equals = a: b: b == anyString || a == b;
+ fallback = { name = "name"; };
+ in findFirst ({ path, ... }: all (v: v == true) (zipListsWith equals loc path)) fallback nameAttrs;
+ nameAttr = matched.name;
+ nameValueOld = value:
+ if isList value then
+ if length value > 0 then
+ "[ " + concatMapStringsSep " " escapeNixString value + " ]"
+ else
+ "[ ]"
+ else
+ escapeNixString value;
+ nameValueNew = value: unnamed:
+ if isList value then
+ if length value > 0 then
+ head value
+ else
+ unnamed
+ else
+ value;
res =
{ inherit (def) file;
value = listToAttrs (
imap1 (elemIdx: elem:
- { name = elem.name or (unnamed elemIdx);
+ { name = nameValueNew (elem.${nameAttr} or (unnamed elemIdx)) (unnamed elemIdx);
value = elem;
}) def.value);
};
option = concatStringsSep "." loc;
sample = take 3 def.value;
- list = concatMapStrings (x: ''{ name = "${x.name or "unnamed"}"; ...} '') sample;
- set = concatMapStrings (x: ''${x.name or "unnamed"} = {...}; '') sample;
+ more = lib.optionalString (length def.value > 3) "... ";
+ list = concatMapStrings (x: ''{ ${nameAttr} = ${nameValueOld (x.${nameAttr} or "unnamed")}; ...} '') sample;
+ set = concatMapStrings (x: ''${nameValueNew (x.${nameAttr} or "unnamed") "unnamed"} = {...}; '') sample;
msg = ''
In file ${def.file}
a list is being assigned to the option config.${option}.
This will soon be an error as type loaOf is deprecated.
- See https://git.io/fj2zm for more information.
+ See https://github.com/NixOS/nixpkgs/pull/63103 for more information.
Do
${option} =
- { ${set}...}
+ { ${set}${more}}
instead of
${option} =
- [ ${list}...]
+ [ ${list}${more}]
'';
in
lib.warn msg res
@@ -339,6 +424,7 @@ rec {
description = "list or attribute set of ${elemType.description}s";
check = x: isList x || isAttrs x;
merge = loc: defs: attrOnly.merge loc (convertAllLists loc defs);
+ emptyValue = { value = {}; };
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: loaOf (elemType.substSubModules m);
@@ -350,6 +436,7 @@ rec {
name = "uniq";
inherit (elemType) description check;
merge = mergeOneOption;
+ emptyValue = elemType.emptyValue;
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m);
@@ -367,6 +454,7 @@ rec {
else if nrNulls != 0 then
throw "The option `${showOption loc}` is defined both null and not null, in ${showFiles (getFiles defs)}."
else elemType.merge loc defs;
+ emptyValue = { value = null; };
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: nullOr (elemType.substSubModules m);
@@ -392,14 +480,16 @@ rec {
else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);
allModules = defs: modules ++ imap1 (n: { value, file }:
- # Annotate the value with the location of its definition for better error messages
- coerce (lib.modules.unifyModuleSyntax file "${toString file}-${toString n}") value
+ if isAttrs value || isFunction value then
+ # Annotate the value with the location of its definition for better error messages
+ coerce (lib.modules.unifyModuleSyntax file "${toString file}-${toString n}") value
+ else value
) defs;
in
mkOptionType rec {
name = "submodule";
- check = x: isAttrs x || isFunction x;
+ check = x: isAttrs x || isFunction x || path.check x;
merge = loc: defs:
(evalModules {
modules = allModules defs;
@@ -407,6 +497,7 @@ rec {
args.name = last loc;
prefix = loc;
}).config;
+ emptyValue = { value = {}; };
getSubOptions = prefix: (evalModules
{ inherit modules prefix specialArgs;
# This is a work-around due to the fact that some sub-modules,
@@ -499,7 +590,7 @@ rec {
tail' = tail ts;
in foldl' either head' tail';
- # Either value of type `finalType` or `coercedType`, the latter is
+ # Either value of type `coercedType` or `finalType`, the former is
# converted to `finalType` using `coerceFunc`.
coercedTo = coercedType: coerceFunc: finalType:
assert lib.assertMsg (coercedType.getSubModules == null)
@@ -508,13 +599,14 @@ rec {
mkOptionType rec {
name = "coercedTo";
description = "${finalType.description} or ${coercedType.description} convertible to it";
- check = x: finalType.check x || (coercedType.check x && finalType.check (coerceFunc x));
+ check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x;
merge = loc: defs:
let
coerceVal = val:
- if finalType.check val then val
- else coerceFunc val;
+ if coercedType.check val then coerceFunc val
+ else val;
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
+ emptyValue = finalType.emptyValue;
getSubOptions = finalType.getSubOptions;
getSubModules = finalType.getSubModules;
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);