Loading lib/modules.nix +29 −9 Original line number Diff line number Diff line Loading @@ -1165,7 +1165,12 @@ let # Type-check the remaining definitions, and merge them. Or throw if no definitions. mergedValue = if isDefined then if type.checkAndMerge or null != null then if type.merge ? v2 then # check and merge share the same closure # .headError is either non-present null or an error describing string if checkedAndMerged.headError or null != null then throw "A definition for option `${showOption loc}' is not of type `${type.description}'. TypeError: ${checkedAndMerged.headError}" else checkedAndMerged.value else if all (def: type.check def.value) defsFinal then type.merge loc defsFinal Loading @@ -1180,15 +1185,30 @@ let throw "The option `${showOption loc}' was accessed but has no value defined. Try setting the option."; checkedAndMerged = if type.checkAndMerge or null != null then type.checkAndMerge loc defsFinal ensureMergedValueEnvelope = v: if attrNames v == attrNames defaultCheckedAndMerged then v else { throw "Invalid 'type.merge.v2' of type: '${type.name}' must return exactly the following attributes: ${builtins.toJSON (attrNames defaultCheckedAndMerged)} but got ${builtins.toJSON (attrNames v)}"; defaultCheckedAndMerged = { headError = null; value = mergedValue; valueMeta = { }; }; checkedAndMerged = ensureMergedValueEnvelope ( if type.merge ? v2 then type.merge.v2 { inherit loc; defs = defsFinal; } else defaultCheckedAndMerged ); isDefined = defsFinal != [ ]; optionalValue = if isDefined then { value = mergedValue; } else { }; Loading lib/types.nix +164 −106 Original line number Diff line number Diff line Loading @@ -212,10 +212,6 @@ let # definition values and locations (e.g. [ { file = "/foo.nix"; # value = 1; } { file = "/bar.nix"; value = 2 } ]). merge ? mergeDefaultOption, # # This field does not have a default implementation, so that users' changes # to `check` and `merge` are propagated. checkAndMerge ? null, # 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 {} Loading Loading @@ -264,7 +260,6 @@ let deprecationMessage nestedTypes descriptionClass checkAndMerge ; functor = if functor ? wrappedDeprecationMessage then Loading Loading @@ -718,9 +713,12 @@ let }"; descriptionClass = "composite"; check = isList; merge = loc: defs: (checkAndMerge loc defs).value; checkAndMerge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let evals = filter (x: x.optionalValue ? value) ( concatLists ( Loading @@ -740,9 +738,11 @@ let ); in { headError = checkDefsForError check loc defs; value = map (x: x.optionalValue.value or x.mergedValue) evals; valueMeta.list = map (v: v.checkedAndMerged.valueMeta) evals; }; }; emptyValue = { value = [ ]; }; Loading Loading @@ -828,9 +828,12 @@ let + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}"; descriptionClass = "composite"; check = isAttrs; merge = loc: defs: (checkAndMerge loc defs).value; checkAndMerge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let evals = if lazy then Loading @@ -843,6 +846,7 @@ let ); in { headError = checkDefsForError check loc defs; value = mapAttrs ( n: v: if lazy then Loading @@ -852,6 +856,7 @@ let ) evals; valueMeta.attrs = mapAttrs (n: v: v.checkedAndMerged.valueMeta) evals; }; }; emptyValue = { value = { }; Loading Loading @@ -1234,6 +1239,7 @@ let name = "submodule"; check = x: isAttrs x || isFunction x || path.check x; in mkOptionType { inherit name; Loading @@ -1245,15 +1251,13 @@ let docsEval = base.extendModules { modules = [ noCheckForDocsModule ]; }; in docsEval._module.freeformType.description or name; check = x: isAttrs x || isFunction x || path.check x; merge = loc: defs: (base.extendModules { modules = [ { _module.args.name = last loc; } ] ++ allModules defs; prefix = loc; }).config; checkAndMerge = loc: defs: inherit check; merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let configuration = base.extendModules { modules = [ { _module.args.name = last loc; } ] ++ allModules defs; Loading @@ -1261,9 +1265,11 @@ let }; in { headError = checkDefsForError check loc defs; value = configuration.config; valueMeta = configuration; }; }; emptyValue = { value = { }; }; Loading Loading @@ -1411,17 +1417,48 @@ let }"; descriptionClass = "conjunction"; check = x: t1.check x || t2.check x; merge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let defList = map (d: d.value) defs; in if all (x: t1.check x) defList then t1.merge loc defs else if all (x: t2.check x) defList then t2.merge loc defs t1CheckedAndMerged = if t1.merge ? v2 then t1.merge.v2 { inherit loc defs; } else { value = t1.merge loc defs; headError = checkDefsForError t1.check loc defs; valueMeta = { }; }; t2CheckedAndMerged = if t2.merge ? v2 then t2.merge.v2 { inherit loc defs; } else mergeOneOption loc defs; { value = t2.merge loc defs; headError = checkDefsForError t2.check loc defs; valueMeta = { }; }; checkedAndMerged = if t1CheckedAndMerged.headError == null then t1CheckedAndMerged else if t2CheckedAndMerged.headError == null then t2CheckedAndMerged else rec { valueMeta = { inherit headError; }; headError = "The option `${showOption loc}` is neither a value of type `${t1.description}` nor `${t2.description}`, Definition values: ${showDefs defs}"; value = null; }; in checkedAndMerged; }; typeMerge = f': let Loading Loading @@ -1462,12 +1499,43 @@ let optionDescriptionPhrase (class: class == "noun") coercedType } convertible to it"; check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x; merge = loc: defs: merge = { __funtor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let isMergeV2 = coercedType.merge ? v2; coerceDef = def: let coerceVal = val: if coercedType.check val then coerceFunc val else val; merged = coercedType.merge.v2 { inherit loc; defs = [ def ]; }; in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); if isMergeV2 then if merged.headError == null then coerceFunc def.value else def.value else if coercedType.check def.value then coerceFunc def.value else def.value; finalDefs = (map (def: def // { value = coerceDef def; }) defs); in if finalType.merge ? v2 then finalType.merge.v2 { inherit loc; defs = finalDefs; } else { value = finalType.merge loc finalDefs; valueMeta = { }; headError = checkDefsForError check loc finalDefs; }; }; emptyValue = finalType.emptyValue; getSubOptions = finalType.getSubOptions; getSubModules = finalType.getSubModules; Loading @@ -1490,25 +1558,15 @@ let */ addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; } // (lib.optionalAttrs (elemType.checkAndMerge != null) { checkAndMerge = loc: defs: let v = (elemType.checkAndMerge loc defs); in if all (def: elemType.check def.value && check def.value) defs then v else let allInvalid = filter (def: !elemType.check def.value || !check def.value) defs; final = elemType // { check = x: elemType.check x && check x; # addCheck discards the merge.v2 function # merge = if elemType.merge ? v2 then elemType.merge.__functor { inherit final; } else elemType.merge; }; in throw "A definition for option `${showOption loc}' is not of type `${elemType.description}'. Definition values:${showDefs allInvalid}"; }); final; }; /** Loading Loading
lib/modules.nix +29 −9 Original line number Diff line number Diff line Loading @@ -1165,7 +1165,12 @@ let # Type-check the remaining definitions, and merge them. Or throw if no definitions. mergedValue = if isDefined then if type.checkAndMerge or null != null then if type.merge ? v2 then # check and merge share the same closure # .headError is either non-present null or an error describing string if checkedAndMerged.headError or null != null then throw "A definition for option `${showOption loc}' is not of type `${type.description}'. TypeError: ${checkedAndMerged.headError}" else checkedAndMerged.value else if all (def: type.check def.value) defsFinal then type.merge loc defsFinal Loading @@ -1180,15 +1185,30 @@ let throw "The option `${showOption loc}' was accessed but has no value defined. Try setting the option."; checkedAndMerged = if type.checkAndMerge or null != null then type.checkAndMerge loc defsFinal ensureMergedValueEnvelope = v: if attrNames v == attrNames defaultCheckedAndMerged then v else { throw "Invalid 'type.merge.v2' of type: '${type.name}' must return exactly the following attributes: ${builtins.toJSON (attrNames defaultCheckedAndMerged)} but got ${builtins.toJSON (attrNames v)}"; defaultCheckedAndMerged = { headError = null; value = mergedValue; valueMeta = { }; }; checkedAndMerged = ensureMergedValueEnvelope ( if type.merge ? v2 then type.merge.v2 { inherit loc; defs = defsFinal; } else defaultCheckedAndMerged ); isDefined = defsFinal != [ ]; optionalValue = if isDefined then { value = mergedValue; } else { }; Loading
lib/types.nix +164 −106 Original line number Diff line number Diff line Loading @@ -212,10 +212,6 @@ let # definition values and locations (e.g. [ { file = "/foo.nix"; # value = 1; } { file = "/bar.nix"; value = 2 } ]). merge ? mergeDefaultOption, # # This field does not have a default implementation, so that users' changes # to `check` and `merge` are propagated. checkAndMerge ? null, # 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 {} Loading Loading @@ -264,7 +260,6 @@ let deprecationMessage nestedTypes descriptionClass checkAndMerge ; functor = if functor ? wrappedDeprecationMessage then Loading Loading @@ -718,9 +713,12 @@ let }"; descriptionClass = "composite"; check = isList; merge = loc: defs: (checkAndMerge loc defs).value; checkAndMerge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let evals = filter (x: x.optionalValue ? value) ( concatLists ( Loading @@ -740,9 +738,11 @@ let ); in { headError = checkDefsForError check loc defs; value = map (x: x.optionalValue.value or x.mergedValue) evals; valueMeta.list = map (v: v.checkedAndMerged.valueMeta) evals; }; }; emptyValue = { value = [ ]; }; Loading Loading @@ -828,9 +828,12 @@ let + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}"; descriptionClass = "composite"; check = isAttrs; merge = loc: defs: (checkAndMerge loc defs).value; checkAndMerge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let evals = if lazy then Loading @@ -843,6 +846,7 @@ let ); in { headError = checkDefsForError check loc defs; value = mapAttrs ( n: v: if lazy then Loading @@ -852,6 +856,7 @@ let ) evals; valueMeta.attrs = mapAttrs (n: v: v.checkedAndMerged.valueMeta) evals; }; }; emptyValue = { value = { }; Loading Loading @@ -1234,6 +1239,7 @@ let name = "submodule"; check = x: isAttrs x || isFunction x || path.check x; in mkOptionType { inherit name; Loading @@ -1245,15 +1251,13 @@ let docsEval = base.extendModules { modules = [ noCheckForDocsModule ]; }; in docsEval._module.freeformType.description or name; check = x: isAttrs x || isFunction x || path.check x; merge = loc: defs: (base.extendModules { modules = [ { _module.args.name = last loc; } ] ++ allModules defs; prefix = loc; }).config; checkAndMerge = loc: defs: inherit check; merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let configuration = base.extendModules { modules = [ { _module.args.name = last loc; } ] ++ allModules defs; Loading @@ -1261,9 +1265,11 @@ let }; in { headError = checkDefsForError check loc defs; value = configuration.config; valueMeta = configuration; }; }; emptyValue = { value = { }; }; Loading Loading @@ -1411,17 +1417,48 @@ let }"; descriptionClass = "conjunction"; check = x: t1.check x || t2.check x; merge = loc: defs: merge = { __functor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let defList = map (d: d.value) defs; in if all (x: t1.check x) defList then t1.merge loc defs else if all (x: t2.check x) defList then t2.merge loc defs t1CheckedAndMerged = if t1.merge ? v2 then t1.merge.v2 { inherit loc defs; } else { value = t1.merge loc defs; headError = checkDefsForError t1.check loc defs; valueMeta = { }; }; t2CheckedAndMerged = if t2.merge ? v2 then t2.merge.v2 { inherit loc defs; } else mergeOneOption loc defs; { value = t2.merge loc defs; headError = checkDefsForError t2.check loc defs; valueMeta = { }; }; checkedAndMerged = if t1CheckedAndMerged.headError == null then t1CheckedAndMerged else if t2CheckedAndMerged.headError == null then t2CheckedAndMerged else rec { valueMeta = { inherit headError; }; headError = "The option `${showOption loc}` is neither a value of type `${t1.description}` nor `${t2.description}`, Definition values: ${showDefs defs}"; value = null; }; in checkedAndMerged; }; typeMerge = f': let Loading Loading @@ -1462,12 +1499,43 @@ let optionDescriptionPhrase (class: class == "noun") coercedType } convertible to it"; check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x; merge = loc: defs: merge = { __funtor = self: loc: defs: (self.v2 { inherit loc defs; }).value; v2 = { loc, defs }: let isMergeV2 = coercedType.merge ? v2; coerceDef = def: let coerceVal = val: if coercedType.check val then coerceFunc val else val; merged = coercedType.merge.v2 { inherit loc; defs = [ def ]; }; in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); if isMergeV2 then if merged.headError == null then coerceFunc def.value else def.value else if coercedType.check def.value then coerceFunc def.value else def.value; finalDefs = (map (def: def // { value = coerceDef def; }) defs); in if finalType.merge ? v2 then finalType.merge.v2 { inherit loc; defs = finalDefs; } else { value = finalType.merge loc finalDefs; valueMeta = { }; headError = checkDefsForError check loc finalDefs; }; }; emptyValue = finalType.emptyValue; getSubOptions = finalType.getSubOptions; getSubModules = finalType.getSubModules; Loading @@ -1490,25 +1558,15 @@ let */ addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; } // (lib.optionalAttrs (elemType.checkAndMerge != null) { checkAndMerge = loc: defs: let v = (elemType.checkAndMerge loc defs); in if all (def: elemType.check def.value && check def.value) defs then v else let allInvalid = filter (def: !elemType.check def.value || !check def.value) defs; final = elemType // { check = x: elemType.check x && check x; # addCheck discards the merge.v2 function # merge = if elemType.merge ? v2 then elemType.merge.__functor { inherit final; } else elemType.merge; }; in throw "A definition for option `${showOption loc}' is not of type `${elemType.description}'. Definition values:${showDefs allInvalid}"; }); final; }; /** Loading