summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2020-09-04 13:18:22 +0200
committerSilvan Mosberger <contact@infinisil.com>2020-09-15 21:01:07 +0200
commit67551f46fbc20b3b96ff27503b659a8f3fedb421 (patch)
tree7c9727af4b245ab77eb83ec5025c7c532a246271 /lib
parent6e7bc2c6c90335e7bb7d7ca8cef77c58f0e37444 (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
Diffstat (limited to 'lib')
-rw-r--r--lib/types.nix36
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";
};