lib.types.attrTag: init
This commit is contained in:
parent
80afdbf97f
commit
ca81a89839
|
@ -103,6 +103,17 @@ checkConfigError 'The option .sub.wrong2. does not exist. Definition values:' co
|
||||||
checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
|
checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
|
||||||
checkConfigError '.*A definition for option .bad. is not of type .non-empty .list of .submodule...\.' config.bad ./error-nonEmptyListOf-submodule.nix
|
checkConfigError '.*A definition for option .bad. is not of type .non-empty .list of .submodule...\.' config.bad ./error-nonEmptyListOf-submodule.nix
|
||||||
|
|
||||||
|
# types.attrTag
|
||||||
|
checkConfigOutput '^true$' config.okChecks ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.syntaxError. is not of type .attribute-tagged union of left, right' config.intStrings.syntaxError ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.syntaxError2. is not of type .attribute-tagged union of left, right' config.intStrings.syntaxError2 ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.syntaxError3. is not of type .attribute-tagged union of left, right' config.intStrings.syntaxError3 ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.syntaxError4. is not of type .attribute-tagged union of left, right' config.intStrings.syntaxError4 ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.mergeError. is not of type .attribute-tagged union of left, right' config.intStrings.mergeError ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.badTagError. is not of type .attribute-tagged union of left, right' config.intStrings.badTagError ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .intStrings\.badTagTypeError\.left. is not of type .signed integer.' config.intStrings.badTagTypeError.left ./types-attrTag.nix
|
||||||
|
checkConfigError 'A definition for option .nested\.right\.left. is not of type .signed integer.' config.nested.right.left ./types-attrTag.nix
|
||||||
|
|
||||||
# types.pathInStore
|
# types.pathInStore
|
||||||
checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
|
checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
|
||||||
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
|
checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
|
||||||
|
|
64
lib/tests/modules/types-attrTag.nix
Normal file
64
lib/tests/modules/types-attrTag.nix
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
forceDeep = x: builtins.deepSeq x x;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
intStrings = mkOption {
|
||||||
|
type = types.attrsOf
|
||||||
|
(types.attrTag {
|
||||||
|
left = types.int;
|
||||||
|
right = types.str;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
nested = mkOption {
|
||||||
|
type = types.attrTag {
|
||||||
|
left = types.int;
|
||||||
|
right = types.attrTag {
|
||||||
|
left = types.int;
|
||||||
|
right = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
merged = mkOption {
|
||||||
|
type = types.attrsOf (
|
||||||
|
types.attrTag {
|
||||||
|
yay = types.int;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
okChecks = mkOption {};
|
||||||
|
};
|
||||||
|
imports = [
|
||||||
|
{
|
||||||
|
options.merged = mkOption {
|
||||||
|
type = types.attrsOf (
|
||||||
|
types.attrTag {
|
||||||
|
nay = types.bool;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
intStrings.syntaxError = 1;
|
||||||
|
intStrings.syntaxError2 = {};
|
||||||
|
intStrings.syntaxError3 = { a = true; b = true; };
|
||||||
|
intStrings.syntaxError4 = lib.mkMerge [ { a = true; } { b = true; } ];
|
||||||
|
intStrings.mergeError = lib.mkMerge [ { int = throw "do not eval"; } { string = throw "do not eval"; } ];
|
||||||
|
intStrings.badTagError.rite = throw "do not eval";
|
||||||
|
intStrings.badTagTypeError.left = "bad";
|
||||||
|
intStrings.numberOne.left = 1;
|
||||||
|
intStrings.hello.right = "hello world";
|
||||||
|
nested.right.left = "not a number";
|
||||||
|
merged.negative.nay = false;
|
||||||
|
merged.positive.yay = 100;
|
||||||
|
okChecks =
|
||||||
|
assert config.intStrings.hello.right == "hello world";
|
||||||
|
assert config.intStrings.numberOne.left == 1;
|
||||||
|
assert config.merged.negative.nay == false;
|
||||||
|
assert config.merged.positive.yay == 100;
|
||||||
|
true;
|
||||||
|
};
|
||||||
|
}
|
|
@ -614,6 +614,43 @@ rec {
|
||||||
nestedTypes.elemType = elemType;
|
nestedTypes.elemType = elemType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
attrTag = tags: attrTagWith { inherit tags; };
|
||||||
|
|
||||||
|
attrTagWith = { tags }:
|
||||||
|
let
|
||||||
|
choicesStr = concatMapStringsSep ", " lib.strings.escapeNixIdentifier (attrNames tags);
|
||||||
|
in
|
||||||
|
mkOptionType {
|
||||||
|
name = "attrTag";
|
||||||
|
description = "attribute-tagged union of ${choicesStr}";
|
||||||
|
getSubModules = null;
|
||||||
|
substSubModules = m: attrTagWith { tags = mapAttrs (n: v: v.substSubModules m) tags; };
|
||||||
|
check = v: isAttrs v && length (attrNames v) == 1 && tags?${head (attrNames v)};
|
||||||
|
merge = loc: defs:
|
||||||
|
let
|
||||||
|
choice = head (attrNames (head defs).value);
|
||||||
|
checkedValueDefs = map
|
||||||
|
(def:
|
||||||
|
assert (length (attrNames def.value)) == 1;
|
||||||
|
if (head (attrNames def.value)) != choice
|
||||||
|
then throw "The option `${showOption loc}` is defined both as `${choice}` and `${head (attrNames def.value)}`, in ${showFiles (getFiles defs)}."
|
||||||
|
else { inherit (def) file; value = def.value.${choice}; })
|
||||||
|
defs;
|
||||||
|
in
|
||||||
|
if tags?${choice}
|
||||||
|
then
|
||||||
|
{ ${choice} =
|
||||||
|
(mergeDefinitions
|
||||||
|
(loc ++ [choice])
|
||||||
|
tags.${choice}
|
||||||
|
checkedValueDefs
|
||||||
|
).mergedValue;
|
||||||
|
}
|
||||||
|
else throw "The option `${showOption loc}` is defined as ${lib.strings.escapeNixIdentifier choice}, but ${lib.strings.escapeNixIdentifier choice} is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${showFiles (getFiles defs)}.";
|
||||||
|
nestedTypes = tags;
|
||||||
|
functor = (defaultFunctor "attrTagWith") // { payload = { inherit tags; }; binOp = a: b: { tags = a.tags // b.tags; }; };
|
||||||
|
};
|
||||||
|
|
||||||
uniq = unique { message = ""; };
|
uniq = unique { message = ""; };
|
||||||
|
|
||||||
unique = { message }: type: mkOptionType rec {
|
unique = { message }: type: mkOptionType rec {
|
||||||
|
|
|
@ -334,6 +334,16 @@ Composed types are types that take a type as parameter. `listOf
|
||||||
the line `The option <option path> is defined multiple times.` and before
|
the line `The option <option path> is defined multiple times.` and before
|
||||||
a list of definition locations.
|
a list of definition locations.
|
||||||
|
|
||||||
|
`types.attrTag` *`{ attr1 = t1; attr2 = t2; ... }`*
|
||||||
|
|
||||||
|
: An attribute set containing one attribute, whose name must be picked from
|
||||||
|
the attribute set (`attr1`, etc) and whose value must be of the accompanying
|
||||||
|
type.
|
||||||
|
|
||||||
|
This is one possible representation of what may be called a _tagged union_ or _sum type_.
|
||||||
|
`attrTag` can be thought of as an extension of *`enum`* where the permissible items
|
||||||
|
are attribute names, and each item is paired with a value of a specific type.
|
||||||
|
|
||||||
`types.either` *`t1 t2`*
|
`types.either` *`t1 t2`*
|
||||||
|
|
||||||
: Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
|
: Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user