about summary refs log tree commit diff
path: root/lib/types.nix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/types.nix')
-rw-r--r--lib/types.nix148
1 files changed, 56 insertions, 92 deletions
diff --git a/lib/types.nix b/lib/types.nix
index 156d72ac5e73..0545cd6a3c27 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -3,10 +3,11 @@
 
 let lib = import ./default.nix; in
 
-with import ./lists.nix;
-with import ./attrsets.nix;
-with import ./options.nix;
-with import ./trivial.nix;
+with lib.lists;
+with lib.attrsets;
+with lib.options;
+with lib.trivial;
+with lib.modules;
 
 rec {
 
@@ -20,48 +21,43 @@ rec {
 
 
   # name (name of the type)
-  # check (check the config value. Before returning false it should trace the bad value eg using traceValIfNot)
+  # check (check the config value)
   # merge (default merge function)
-  # iter (iterate on all elements contained in this type)
-  # fold (fold all elements contained in this type)
-  # hasOptions (boolean: whatever this option contains an option set)
-  # delayOnGlobalEval (boolean: should properties go through the evaluation of this option)
   # docPath (path concatenated to the option name contained in the option set)
   isOptionType = isType "option-type";
   mkOptionType =
     { name
     , check ? (x: true)
     , merge ? mergeDefaultOption
-    # Handle complex structure types.
-    , iter ? (f: path: v: f path v)
-    , fold ? (op: nul: v: op v nul)
+    , merge' ? args: merge
     , docPath ? lib.id
-    # If the type can contains option sets.
-    , hasOptions ? false
-    , delayOnGlobalEval ? false
     }:
 
     { _type = "option-type";
-      inherit name check merge iter fold docPath hasOptions delayOnGlobalEval;
+      inherit name check merge merge' docPath;
     };
 
 
   types = rec {
 
+    unspecified = mkOptionType {
+      name = "unspecified";
+    };
+
     bool = mkOptionType {
       name = "boolean";
-      check = lib.traceValIfNot builtins.isBool;
+      check = builtins.isBool;
       merge = fold lib.or false;
     };
 
     int = mkOptionType {
       name = "integer";
-      check = lib.traceValIfNot builtins.isInt;
+      check = builtins.isInt;
     };
 
     string = mkOptionType {
       name = "string";
-      check = lib.traceValIfNot builtins.isString;
+      check = builtins.isString;
       merge = lib.concatStrings;
     };
 
@@ -69,7 +65,7 @@ rec {
     # configuration file contents.
     lines = mkOptionType {
       name = "string";
-      check = lib.traceValIfNot builtins.isString;
+      check = builtins.isString;
       merge = lib.concatStringsSep "\n";
     };
 
@@ -81,48 +77,37 @@ rec {
 
     attrs = mkOptionType {
       name = "attribute set";
-      check = lib.traceValIfNot isAttrs;
+      check = isAttrs;
       merge = fold lib.mergeAttrs {};
     };
 
     # derivation is a reserved keyword.
     package = mkOptionType {
       name = "derivation";
-      check = lib.traceValIfNot isDerivation;
+      check = isDerivation;
     };
 
     path = mkOptionType {
       name = "path";
       # Hacky: there is no ‘isPath’ primop.
-      check = lib.traceValIfNot (x: builtins.unsafeDiscardStringContext (builtins.substring 0 1 (toString x)) == "/");
+      check = x: builtins.unsafeDiscardStringContext (builtins.substring 0 1 (toString x)) == "/";
     };
 
     # drop this in the future:
-    list = builtins.trace "types.list is deprecated, use types.listOf instead" types.listOf;
+    list = builtins.trace "types.list is deprecated; use types.listOf instead" types.listOf;
 
-    listOf = elemType: mkOptionType { 
+    listOf = elemType: mkOptionType {
       name = "list of ${elemType.name}s";
-      check = value: lib.traceValIfNot isList value && all elemType.check value;
-      merge = concatLists;
-      iter = f: path: list: map (elemType.iter f (path + ".*")) list;
-      fold = op: nul: list: lib.fold (e: l: elemType.fold op l e) nul list;
+      check = value: isList value && all elemType.check value;
+      merge = defs: map (def: elemType.merge [def]) (concatLists defs);
       docPath = path: elemType.docPath (path + ".*");
-      inherit (elemType) hasOptions;
-
-      # You cannot define multiple configurations of one entity, therefore
-      # no reason justify to delay properties inside list elements.
-      delayOnGlobalEval = false;
     };
 
     attrsOf = elemType: mkOptionType {
       name = "attribute set of ${elemType.name}s";
-      check = x: lib.traceValIfNot isAttrs x
-        && all elemType.check (lib.attrValues x); 
-      merge = lib.zipAttrsWith (name: elemType.merge);
-      iter = f: path: set: lib.mapAttrs (name: elemType.iter f (path + "." + name)) set;
-      fold = op: nul: set: fold (e: l: elemType.fold op l e) nul (lib.attrValues set);
+      check = x: isAttrs x && all elemType.check (lib.attrValues x);
+      merge = lib.zipAttrsWith (name: elemType.merge' { inherit name; });
       docPath = path: elemType.docPath (path + ".<name>");
-      inherit (elemType) hasOptions delayOnGlobalEval;
     };
 
     # List or attribute set of ...
@@ -143,26 +128,13 @@ rec {
         check = x:
           if isList x       then listOnly.check x
           else if isAttrs x then attrOnly.check x
-          else lib.traceValIfNot (x: false) x;
-        ## The merge function returns an attribute set
-        merge = defs:
-          attrOnly.merge (imap convertIfList defs);
-        iter = f: path: def:
-          if isList def       then listOnly.iter f path def
-          else if isAttrs def then attrOnly.iter f path def
-          else throw "Unexpected value";
-        fold = op: nul: def:
-          if isList def       then listOnly.fold op nul def
-          else if isAttrs def then attrOnly.fold op nul def
-          else throw "Unexpected value";
-
+          else false;
+        merge = defs: attrOnly.merge (imap convertIfList defs);
         docPath = path: elemType.docPath (path + ".<name?>");
-        inherit (elemType) hasOptions delayOnGlobalEval;
-      }
-    ;
+      };
 
     uniq = elemType: mkOptionType {
-      inherit (elemType) name check iter fold docPath hasOptions;
+      inherit (elemType) name check docPath;
       merge = list:
         if length list == 1 then
           head list
@@ -171,54 +143,46 @@ rec {
     };
 
     none = elemType: mkOptionType {
-      inherit (elemType) name check iter fold docPath hasOptions;
+      inherit (elemType) name check docPath;
       merge = list:
         throw "No definitions are allowed for this option.";
     };
 
     nullOr = elemType: mkOptionType {
-      inherit (elemType) name merge docPath hasOptions;
+      inherit (elemType) docPath;
+      name = "null or ${elemType.name}";
       check = x: builtins.isNull x || elemType.check x;
-      iter = f: path: v: if v == null then v else elemType.iter f path v;
-      fold = op: nul: v: if v == null then nul else elemType.fold op nul v;
+      merge = defs:
+        if all isNull defs then null
+        else if any isNull defs then
+          throw "Some but not all values are null."
+        else elemType.merge defs;
     };
 
     functionTo = elemType: mkOptionType {
       name = "function that evaluates to a(n) ${elemType.name}";
-      check = lib.traceValIfNot builtins.isFunction;
+      check = builtins.isFunction;
       merge = fns:
         args: elemType.merge (map (fn: fn args) fns);
-      # These are guesses, I don't fully understand iter, fold, delayOnGlobalEval
-      iter = f: path: v:
-        args: elemType.iter f path (v args);
-      fold = op: nul: v:
-        args: elemType.fold op nul (v args);
-      inherit (elemType) delayOnGlobalEval;
-      hasOptions = false;
-    };
-
-    # usually used with listOf, attrsOf, loaOf like this:
-    # users = mkOption {
-    #   type = loaOf optionSet;
-    #
-    #   # you can omit the list if there is one element only
-    #   options = [ {
-    #     name = mkOption {
-    #       description = "name of the user"
-    #       ...
-    #     };
-    #     # more options here
-    #   } { more options } ];
-    # }
-    # TODO: !!! document passing options as an argument to optionSet,
-    # deprecate the current approach.
-    optionSet = mkOptionType {
-      name = "option set";
-      # merge is done in "options.nix > addOptionMakeUp > handleOptionSets"
-      merge = lib.id;
+    };
+
+    submodule = opts: mkOptionType rec {
+      name = "submodule";
       check = x: isAttrs x || builtins.isFunction x;
-      hasOptions = true;
-      delayOnGlobalEval = true;
+      # FIXME: make error messages include the parent attrpath.
+      merge = merge' {};
+      merge' = args: defs:
+        let
+          coerce = def: if builtins.isFunction def then def else { config = def; };
+          modules = (toList opts) ++ map coerce defs;
+        in (evalModules modules args).config;
+    };
+
+    # 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";
     };
 
   };