diff options
Diffstat (limited to 'lib/types.nix')
-rw-r--r-- | lib/types.nix | 217 |
1 files changed, 48 insertions, 169 deletions
diff --git a/lib/types.nix b/lib/types.nix index 385103d42149..b4d29ac84d28 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -1,8 +1,6 @@ # Definitions related to run-time type checking. Used in particular # to type-check NixOS configurations. -let lib = import ./default.nix; in - with import ./lists.nix; with import ./attrsets.nix; with import ./options.nix; @@ -23,8 +21,6 @@ rec { mkOptionType = { # Human-readable representation of the type. name - , # Parseable representation of the type. - typerep , # Function applied to each definition that should return true if # its type-correct, false otherwise. check ? (x: true) @@ -35,59 +31,40 @@ rec { # definition values and locations (e.g. [ { file = "/foo.nix"; # value = 1; } { file = "/bar.nix"; value = 2 } ]). merge ? mergeDefaultOption - , # Return list of sub-options. - getSubOptions ? {} - , # Same as 'getSubOptions', but with extra information about the - # location of the option which is used to generate documentation. - getSubOptionsPrefixed ? null + , # Return a flat list of sub-options. Used to generate + # documentation. + getSubOptions ? prefix: {} , # List of modules if any, or null if none. getSubModules ? null , # Function for building the same option type with a different list of # modules. substSubModules ? m: null - , # List of type representations (typerep) of all the elementary types - # that are nested within the type. For an elementary type the list is - # a singleton of the typerep of itself. - # NOTE: Must be specified for every container type! - nestedTypes ? null - , # List of all default values, and an empty list if no default value exists. - defaultValues ? [] }: { _type = "option-type"; - inherit name typerep check merge getSubOptions getSubModules substSubModules defaultValues; - nestedTypes = if (isNull nestedTypes) then (singleton typerep) else nestedTypes; - getSubOptionsPrefixed = if (isNull getSubOptionsPrefixed) then (prefix: getSubOptions) else getSubOptionsPrefixed; + inherit name check merge getSubOptions getSubModules substSubModules; }; types = rec { - # - # Elementary types - # - unspecified = mkOptionType { name = "unspecified"; - typerep = "(unspecified)"; }; bool = mkOptionType { name = "boolean"; - typerep = "(boolean)"; check = isBool; merge = mergeEqualOption; }; int = mkOptionType { name = "integer"; - typerep = "(integer)"; check = isInt; merge = mergeOneOption; }; str = mkOptionType { name = "string"; - typerep = "(string)"; check = isString; merge = mergeOneOption; }; @@ -95,111 +72,73 @@ rec { # Merge multiple definitions by concatenating them (with the given # separator between the values). separatedString = sep: mkOptionType { - name = "string" + (optionalString (sep != "") " separated by ${sep}"); - typerep = "(separatedString(${escape ["(" ")"] sep}))"; + name = "string"; check = isString; - merge = _module: loc: defs: concatStringsSep sep (getValues defs); + merge = loc: defs: concatStringsSep sep (getValues defs); }; - lines = separatedString "\n" // { typerep = "(lines)"; }; - commas = separatedString "," // { typerep = "(commas)"; }; - envVar = separatedString ":" // { typerep = "(envVar)"; }; + lines = separatedString "\n"; + commas = separatedString ","; + envVar = separatedString ":"; # Deprecated; should not be used because it quietly concatenates # strings, which is usually not what you want. - string = separatedString "" // { typerep = "(string)"; }; + string = separatedString ""; attrs = mkOptionType { name = "attribute set"; - typerep = "(attrs)"; check = isAttrs; - merge = _module: loc: foldl' (res: def: mergeAttrs res def.value) {}; + merge = loc: foldl' (res: def: mergeAttrs res def.value) {}; }; # derivation is a reserved keyword. package = mkOptionType { name = "package"; - typerep = "(package)"; check = x: isDerivation x || isStorePath x; - merge = _module: loc: defs: - let res = mergeOneOption _module loc defs; + merge = loc: defs: + let res = mergeOneOption loc defs; in if isDerivation res then res else toDerivation res; }; - # The correct type of packageSet would be: - # packageSet = attrsOf (either package packageSet) - # (Not sure if nix would allow to define a recursive type.) - # However, currently it is not possible to check that a packageSet actually - # contains packages (that is derivations). The check 'isDerivation' is too - # eager for the current implementation of the assertion mechanism and of the - # licenses control mechanism. That means it is not generally possible to go - # into the attribute set of packages to check that every attribute would - # evaluate to a derivation if the package would actually be evaluated. Maybe - # that restriction can be lifted in the future, but for now the content of - # the packageSet is not checked. - # TODO: The 'merge' function is copied from 'mergeDefaultOption' to keep - # backwards compatibility with the 'unspecified' type that was used for - # package sets previously. Maybe review if the merge function has to change. - packageSet = mkOptionType { - name = "derivation set"; - typerep = "(packageSet)"; - check = isAttrs; - merge = _module: loc: defs: foldl' mergeAttrs {} (map (x: x.value) defs); - }; - path = mkOptionType { name = "path"; - typerep = "(path)"; # Hacky: there is no ‘isPath’ primop. check = x: builtins.substring 0 1 (toString x) == "/"; merge = mergeOneOption; }; - - # - # Container types - # - # drop this in the future: list = builtins.trace "`types.list' is deprecated; use `types.listOf' instead" types.listOf; listOf = elemType: mkOptionType { name = "list of ${elemType.name}s"; - typerep = "(listOf${elemType.typerep})"; check = isList; - merge = _module: loc: defs: + merge = loc: defs: map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def: imap (m: def': - (mergeDefinitions _module + (mergeDefinitions (loc ++ ["[definition ${toString n}-entry ${toString m}]"]) elemType [{ inherit (def) file; value = def'; }] ).optionalValue ) def.value) defs))); - getSubOptions = elemType.getSubOptions; - getSubOptionsPrefixed = prefix: elemType.getSubOptionsPrefixed (prefix ++ ["*"]); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); - nestedTypes = elemType.nestedTypes; - defaultValues = [[]]; }; attrsOf = elemType: mkOptionType { name = "attribute set of ${elemType.name}s"; - typerep = "(attrsOf${elemType.typerep})"; check = isAttrs; - merge = _module: loc: defs: + merge = loc: defs: mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs: - (mergeDefinitions _module (loc ++ [name]) elemType defs).optionalValue + (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))); - getSubOptions = elemType.getSubOptions; - getSubOptionsPrefixed = prefix: elemType.getSubOptionsPrefixed (prefix ++ ["<name>"]); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]); getSubModules = elemType.getSubModules; substSubModules = m: attrsOf (elemType.substSubModules m); - nestedTypes = elemType.nestedTypes; - defaultValues = [{}]; }; # List or attribute set of ... @@ -220,23 +159,18 @@ rec { attrOnly = attrsOf elemType; in mkOptionType { name = "list or attribute set of ${elemType.name}s"; - typerep = "(loaOf${elemType.typerep})"; check = x: isList x || isAttrs x; - merge = _module: loc: defs: attrOnly.merge _module loc (imap convertIfList defs); - getSubOptions = elemType.getSubOptions; - getSubOptionsPrefixed = prefix: elemType.getSubOptionsPrefixed (prefix ++ ["<name?>"]); + merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]); getSubModules = elemType.getSubModules; substSubModules = m: loaOf (elemType.substSubModules m); - nestedTypes = elemType.nestedTypes; - defaultValues = [{} []]; }; # List or element of ... loeOf = elemType: mkOptionType { name = "element or list of ${elemType.name}s"; - typerep = "(loeOf${elemType.typerep})"; check = x: isList x || elemType.check x; - merge = _module: loc: defs: + merge = loc: defs: let defs' = filterOverrides defs; res = (head defs').value; @@ -247,136 +181,81 @@ rec { else if !isString res then throw "The option `${showOption loc}' does not have a string value, in ${showFiles (getFiles defs)}." else res; - nestedTypes = elemType.nestedTypes; - defaultValues = [[]] ++ elemType.defaultValues; }; uniq = elemType: mkOptionType { - inherit (elemType) check; - name = "unique ${elemType.name}"; - typerep = "(uniq${elemType.typerep})"; + inherit (elemType) name check; merge = mergeOneOption; getSubOptions = elemType.getSubOptions; - getSubOptionsPrefixed = prefix: elemType.getSubOptionsPrefixed prefix; getSubModules = elemType.getSubModules; substSubModules = m: uniq (elemType.substSubModules m); - nestedTypes = elemType.nestedTypes; - defaultValues = elemType.defaultValues; }; nullOr = elemType: mkOptionType { name = "null or ${elemType.name}"; - typerep = "(nullOr${elemType.typerep})"; check = x: x == null || elemType.check x; - merge = _module: loc: defs: + merge = loc: defs: let nrNulls = count (def: def.value == null) defs; in if nrNulls == length defs then null else if nrNulls != 0 then throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}." - else elemType.merge _module loc defs; + else elemType.merge loc defs; getSubOptions = elemType.getSubOptions; - getSubOptionsPrefixed = prefix: elemType.getSubOptionsPrefixed prefix; getSubModules = elemType.getSubModules; substSubModules = m: nullOr (elemType.substSubModules m); - nestedTypes = elemType.nestedTypes; - defaultValues = [null] ++ elemType.defaultValues; - }; - - enum = values: - let - show = v: - if builtins.isString v then ''"${v}"'' - else if builtins.isInt v then builtins.toString v - else ''<${builtins.typeOf v}>''; - in - mkOptionType { - name = "one of ${concatMapStringsSep ", " show values}"; - typerep = "(enum${concatMapStrings (x: "(${escape ["(" ")"] (builtins.toString x)})") values})"; - check = flip elem values; - merge = mergeOneOption; - nestedTypes = []; - }; - - either = t1: t2: mkOptionType { - name = "${t1.name} or ${t2.name}"; - typerep = "(either${t1.typerep}${t2.typerep})"; - check = x: t1.check x || t2.check x; - merge = mergeOneOption; - nestedTypes = t1.nestedTypes ++ t2.nestedTypes; - defaultValues = t1.defaultValues ++ t2.defaultValues; }; - - # - # Complex types - # - submodule = opts: let opts' = toList opts; inherit (import ./modules.nix) evalModules; - filterVisible = filter (opt: (if opt ? visible then opt.visible else true) && (if opt ? internal then !opt.internal else true)); in mkOptionType rec { name = "submodule"; - typerep = "(submodule)"; check = x: isAttrs x || isFunction x; - merge = _module: loc: defs: + merge = loc: defs: let - internalModule = [ { inherit _module; } { _module.args.name = lib.mkForce (last loc); } ]; coerce = def: if isFunction def then def else { config = def; }; - modules = opts' ++ internalModule ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs; + modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs; in (evalModules { inherit modules; + args.name = last loc; prefix = loc; }).config; - getSubOptions = getSubOptionsPrefixed []; - getSubOptionsPrefixed = prefix: _module: - let + getSubOptions = prefix: (evalModules + { modules = opts'; inherit prefix; # FIXME: hack to get shit to evaluate. - internalModule = [ { inherit _module; } { _module.args.name = lib.mkForce ""; } ]; - in (evalModules { - modules = opts' ++ internalModule; - inherit prefix; - }).options; + args = { name = ""; }; }).options; getSubModules = opts'; substSubModules = m: submodule m; - nestedTypes = concatMap (opt: opt.type.nestedTypes) (collect (lib.isType "option") (getSubOptions {})); - defaultValues = [{}]; }; + enum = values: + let + show = v: + if builtins.isString v then ''"${v}"'' + else if builtins.isInt v then builtins.toString v + else ''<${builtins.typeOf v}>''; + in + mkOptionType { + name = "one of ${concatMapStringsSep ", " show values}"; + check = flip elem values; + merge = mergeOneOption; + }; - - # - # Legacy types - # + either = t1: t2: mkOptionType { + name = "${t1.name} or ${t2.name}"; + check = x: t1.check x || t2.check x; + merge = mergeOneOption; + }; # Obsolete alternative to configOf. It takes its option # declarations from the ‘options’ attribute of containing option # declaration. optionSet = mkOptionType { name = /* builtins.trace "types.optionSet is deprecated; use types.submodule instead" */ "option set"; - typerep = "(optionSet)"; }; - - # Try to remove module options of that type wherever possible. - # A module option taking a function can not be introspected and documented properly. - functionTo = resultType: - mkOptionType { - name = "function to ${resultType.name}"; - typerep = "(function${resultType.typerep})"; - check = builtins.isFunction; - merge = mergeOneOption; - nestedTypes = resultType.nestedTypes; - defaultValues = map (x: {...}:x) resultType.nestedTypes; # INFO: It seems as nix can't compare functions, yet. - }; - - - # - # misc - # - # Augment the given type with an additional type check function. addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; }; |