mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-02-27 06:25:23 +00:00
Merge pull request #284512 from hercules-ci/lib-types-unique-merge
lib.types.unique: Check inner type deeply
This commit is contained in:
commit
f37ba19765
@ -254,13 +254,31 @@ rec {
|
|||||||
else if all isInt list && all (x: x == head list) list then head list
|
else if all isInt list && all (x: x == head list) list then head list
|
||||||
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Require a single definition.
|
||||||
|
|
||||||
|
WARNING: Does not perform nested checks, as this does not run the merge function!
|
||||||
|
*/
|
||||||
mergeOneOption = mergeUniqueOption { message = ""; };
|
mergeOneOption = mergeUniqueOption { message = ""; };
|
||||||
|
|
||||||
mergeUniqueOption = { message }: loc: defs:
|
/*
|
||||||
if length defs == 1
|
Require a single definition.
|
||||||
then (head defs).value
|
|
||||||
else assert length defs > 1;
|
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
|
||||||
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
*/
|
||||||
|
mergeUniqueOption = args@{
|
||||||
|
message,
|
||||||
|
# WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
|
||||||
|
# - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc)
|
||||||
|
# - if you want attribute values to be checked, or list items
|
||||||
|
# - if you want coercedTo-like behavior to work
|
||||||
|
merge ? loc: defs: (head defs).value }:
|
||||||
|
loc: defs:
|
||||||
|
if length defs == 1
|
||||||
|
then merge loc defs
|
||||||
|
else
|
||||||
|
assert length defs > 1;
|
||||||
|
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
||||||
|
|
||||||
/* "Merge" option definitions by checking that they all have the same value. */
|
/* "Merge" option definitions by checking that they all have the same value. */
|
||||||
mergeEqualOption = loc: defs:
|
mergeEqualOption = loc: defs:
|
||||||
|
@ -407,6 +407,16 @@ checkConfigOutput "{}" config.submodule.a ./emptyValues.nix
|
|||||||
checkConfigError 'The option .int.a. is used but not defined' config.int.a ./emptyValues.nix
|
checkConfigError 'The option .int.a. is used but not defined' config.int.a ./emptyValues.nix
|
||||||
checkConfigError 'The option .nonEmptyList.a. is used but not defined' config.nonEmptyList.a ./emptyValues.nix
|
checkConfigError 'The option .nonEmptyList.a. is used but not defined' config.nonEmptyList.a ./emptyValues.nix
|
||||||
|
|
||||||
|
# types.unique
|
||||||
|
# requires a single definition
|
||||||
|
checkConfigError 'The option .examples\.merged. is defined multiple times while it.s expected to be unique' config.examples.merged.a ./types-unique.nix
|
||||||
|
# user message is printed
|
||||||
|
checkConfigError 'We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system.' config.examples.merged.a ./types-unique.nix
|
||||||
|
# let the inner merge function check the values (on demand)
|
||||||
|
checkConfigError 'A definition for option .examples\.badLazyType\.a. is not of type .string.' config.examples.badLazyType.a ./types-unique.nix
|
||||||
|
# overriding still works (unlike option uniqueness)
|
||||||
|
checkConfigOutput '^"bee"$' config.examples.override.b ./types-unique.nix
|
||||||
|
|
||||||
## types.raw
|
## types.raw
|
||||||
checkConfigOutput '^true$' config.unprocessedNestingEvaluates.success ./raw.nix
|
checkConfigOutput '^true$' config.unprocessedNestingEvaluates.success ./raw.nix
|
||||||
checkConfigOutput "10" config.processedToplevel ./raw.nix
|
checkConfigOutput "10" config.processedToplevel ./raw.nix
|
||||||
|
27
lib/tests/modules/types-unique.nix
Normal file
27
lib/tests/modules/types-unique.nix
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.examples = mkOption {
|
||||||
|
type = types.lazyAttrsOf
|
||||||
|
(types.unique
|
||||||
|
{ message = "We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system."; }
|
||||||
|
(types.attrsOf types.str));
|
||||||
|
};
|
||||||
|
imports = [
|
||||||
|
{ examples.merged = { b = "bee"; }; }
|
||||||
|
{ examples.override = lib.mkForce { b = "bee"; }; }
|
||||||
|
];
|
||||||
|
config.examples = {
|
||||||
|
merged = {
|
||||||
|
a = "aye";
|
||||||
|
};
|
||||||
|
override = {
|
||||||
|
a = "aye";
|
||||||
|
};
|
||||||
|
badLazyType = {
|
||||||
|
a = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -614,23 +614,12 @@ rec {
|
|||||||
nestedTypes.elemType = elemType;
|
nestedTypes.elemType = elemType;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Value of given type but with no merging (i.e. `uniq list`s are not concatenated).
|
uniq = unique { message = ""; };
|
||||||
uniq = elemType: mkOptionType rec {
|
|
||||||
name = "uniq";
|
|
||||||
inherit (elemType) description descriptionClass check;
|
|
||||||
merge = mergeOneOption;
|
|
||||||
emptyValue = elemType.emptyValue;
|
|
||||||
getSubOptions = elemType.getSubOptions;
|
|
||||||
getSubModules = elemType.getSubModules;
|
|
||||||
substSubModules = m: uniq (elemType.substSubModules m);
|
|
||||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
|
||||||
nestedTypes.elemType = elemType;
|
|
||||||
};
|
|
||||||
|
|
||||||
unique = { message }: type: mkOptionType rec {
|
unique = { message }: type: mkOptionType rec {
|
||||||
name = "unique";
|
name = "unique";
|
||||||
inherit (type) description descriptionClass check;
|
inherit (type) description descriptionClass check;
|
||||||
merge = mergeUniqueOption { inherit message; };
|
merge = mergeUniqueOption { inherit message; inherit (type) merge; };
|
||||||
emptyValue = type.emptyValue;
|
emptyValue = type.emptyValue;
|
||||||
getSubOptions = type.getSubOptions;
|
getSubOptions = type.getSubOptions;
|
||||||
getSubModules = type.getSubModules;
|
getSubModules = type.getSubModules;
|
||||||
|
@ -326,7 +326,7 @@ Composed types are types that take a type as parameter. `listOf
|
|||||||
`types.uniq` *`t`*
|
`types.uniq` *`t`*
|
||||||
|
|
||||||
: Ensures that type *`t`* cannot be merged. It is used to ensure option
|
: Ensures that type *`t`* cannot be merged. It is used to ensure option
|
||||||
definitions are declared only once.
|
definitions are provided only once.
|
||||||
|
|
||||||
`types.unique` `{ message = m }` *`t`*
|
`types.unique` `{ message = m }` *`t`*
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user