diff options
author | Silvan Mosberger <contact@infinisil.com> | 2020-09-04 13:18:22 +0200 |
---|---|---|
committer | Silvan Mosberger <contact@infinisil.com> | 2020-09-15 21:01:07 +0200 |
commit | 67551f46fbc20b3b96ff27503b659a8f3fedb421 (patch) | |
tree | 7c9727af4b245ab77eb83ec5025c7c532a246271 | |
parent | 6e7bc2c6c90335e7bb7d7ca8cef77c58f0e37444 (diff) |
lib/types: Introduce types.anything
This new type has unsurprising merge behavior: Only attribute sets are
merged together (recursively), and only if they don't conflict.
This is in contrast to the existing types:
- types.attrs is problematic because later definitions completely
override attributes of earlier definitions, and it doesn't support
mkIf and co.
- types.unspecified is very similar to types.attrs, but it has smart
merging behavior that often doesn't make sense, and it doesn't support
all types
-rw-r--r-- | lib/types.nix | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/lib/types.nix b/lib/types.nix index 17e7a939fe3d..a5f4d87e3efd 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -101,6 +101,42 @@ rec { # When adding new types don't forget to document them in # nixos/doc/manual/development/option-types.xml! types = rec { + + anything = mkOptionType { + name = "anything"; + description = "anything"; + check = value: true; + merge = loc: defs: + let + getType = value: + if isAttrs value && isCoercibleToString value + then "stringCoercibleSet" + else builtins.typeOf value; + + # Returns the common type of all definitions, throws an error if they + # don't have the same type + commonType = foldl' (type: def: + if getType def.value == type + then type + else throw "The option `${showOption loc}' has conflicting option types in ${showFiles (getFiles defs)}" + ) (getType (head defs).value) defs; + + mergeFunction = { + # Recursively merge attribute sets + set = (attrsOf anything).merge; + # Safe and deterministic behavior for lists is to only accept one definition + # listOf only used to apply mkIf and co. + list = + if length defs > 1 + then throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}." + else (listOf anything).merge; + # This is the type of packages, only accept a single definition + stringCoercibleSet = mergeOneOption; + # Otherwise fall back to only allowing all equal definitions + }.${commonType} or mergeEqualOption; + in mergeFunction loc defs; + }; + unspecified = mkOptionType { name = "unspecified"; }; |