diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 7618fd23383d..8238391d3f0b 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -2612,4 +2612,45 @@ runTests { }; expected = "c"; }; + + testMergeTypesSimple = + let + mergedType = types.mergeTypes types.str types.str; + in + { + expr = mergedType.name; + expected = "str"; + }; + + testMergeTypesFail = + let + mergedType = types.mergeTypes types.str types.int; + in + { + expr = types.isType "merge-error" mergedType; + expected = true; + }; + + testMergeTypesEnum = + let + enumAB = lib.types.enum ["A" "B"]; + enumXY = lib.types.enum ["X" "Y"]; + merged = lib.types.mergeTypes enumAB enumXY; # -> enum [ "A" "B" "X" "Y" ] + in + { + expr = { + checkA = merged.check "A"; + checkB = merged.check "B"; + checkX = merged.check "X"; + checkY = merged.check "Y"; + checkC = merged.check "C"; + }; + expected = { + checkA = true; + checkB = true; + checkX = true; + checkY = true; + checkC = false; + }; + }; } diff --git a/lib/types.nix b/lib/types.nix index e74775d0a27a..5226609822f1 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -1125,6 +1125,53 @@ rec { addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; }; }; + + /** + Merges two option types together. + + :::{.note} + Uses the type merge function of the first type, to merge it with the second type. + + Usually types can only be merged if they are of the same type + ::: + + # Inputs + + : `a` (option type): The first option type. + : `b` (option type): The second option type. + + # Returns + + - The merged option type. + - `{ _type = "merge-error"; error = "Cannot merge types"; }` if the types can't be merged. + + # Examples + :::{.example} + ## `lib.types.mergeTypes` usage example + ```nix + let + enumAB = lib.types.enum ["A" "B"]; + enumXY = lib.types.enum ["X" "Y"]; + # This operation could be notated as: [ A ] | [ B ] -> [ A B ] + merged = lib.types.mergeTypes enumAB enumXY; # -> enum [ "A" "B" "X" "Y" ] + in + assert merged.check "A"; # true + assert merged.check "B"; # true + assert merged.check "X"; # true + assert merged.check "Y"; # true + merged.check "C" # false + ``` + ::: + */ + mergeTypes = a: b: + assert isOptionType a && isOptionType b; + let + merged = a.typeMerge b.functor; + in + if merged == null then + setType "merge-error" { error = "Cannot merge types"; } + else + merged; }; in outer_types // outer_types.types