mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-02-20 11:05:14 +00:00
lib.types.attrsWith: add placeholder parameter
This commit is contained in:
parent
fd6bc1b590
commit
d504a1e680
@ -420,20 +420,17 @@ rec {
|
||||
Placeholders will not be quoted as they are not actual values:
|
||||
(showOption ["foo" "*" "bar"]) == "foo.*.bar"
|
||||
(showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
|
||||
(showOption ["foo" "<myPlaceholder>" "bar"]) == "foo.<myPlaceholder>.bar"
|
||||
*/
|
||||
showOption = parts: let
|
||||
# If the part is a named placeholder of the form "<...>" don't escape it.
|
||||
# It may cause misleading escaping if somebody uses literally "<...>" in their option names.
|
||||
# This is the trade-off to allow for placeholders in option names.
|
||||
isNamedPlaceholder = builtins.match "\<(.*)\>";
|
||||
escapeOptionPart = part:
|
||||
let
|
||||
# We assume that these are "special values" and not real configuration data.
|
||||
# If it is real configuration data, it is rendered incorrectly.
|
||||
specialIdentifiers = [
|
||||
"<name>" # attrsOf (submodule {})
|
||||
"*" # listOf (submodule {})
|
||||
"<function body>" # functionTo
|
||||
];
|
||||
in if builtins.elem part specialIdentifiers
|
||||
then part
|
||||
else lib.strings.escapeNixIdentifier part;
|
||||
if part == "*" || isNamedPlaceholder part != null
|
||||
then part
|
||||
else lib.strings.escapeNixIdentifier part;
|
||||
in (concatStringsSep ".") (map escapeOptionPart parts);
|
||||
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
|
||||
|
||||
|
@ -1877,6 +1877,44 @@ runTests {
|
||||
expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
|
||||
};
|
||||
|
||||
testAttrsWithName = {
|
||||
expr = let
|
||||
eval = evalModules {
|
||||
modules = [
|
||||
{
|
||||
options = {
|
||||
foo = lib.mkOption {
|
||||
type = lib.types.attrsWith {
|
||||
placeholder = "MyCustomPlaceholder";
|
||||
elemType = lib.types.submodule {
|
||||
options.bar = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 42;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
opt = eval.options.foo;
|
||||
in
|
||||
(opt.type.getSubOptions opt.loc).bar.loc;
|
||||
expected = [
|
||||
"foo"
|
||||
"<MyCustomPlaceholder>"
|
||||
"bar"
|
||||
];
|
||||
};
|
||||
|
||||
testShowOptionWithPlaceholder = {
|
||||
# <name>, *, should not be escaped. It is used as a placeholder by convention.
|
||||
# Other symbols should be escaped. `{}`
|
||||
expr = lib.showOption ["<name>" "<myName>" "*" "{foo}"];
|
||||
expected = "<name>.<myName>.*.\"{foo}\"";
|
||||
};
|
||||
|
||||
testCartesianProductOfEmptySet = {
|
||||
expr = cartesianProduct {};
|
||||
expected = [ {} ];
|
||||
|
@ -391,6 +391,10 @@ checkConfigError 'The option `mergedLazyNonLazy'\'' in `.*'\'' is already declar
|
||||
checkConfigOutput '^11$' config.lazyResult ./lazy-attrsWith.nix
|
||||
checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-attrsWith.nix
|
||||
|
||||
# AttrsWith placeholder tests
|
||||
checkConfigOutput '^"mergedName.<id>.nested"$' config.result ./name-merge-attrsWith-1.nix
|
||||
checkConfigError 'The option .mergedName. in .*\.nix. is already declared in .*\.nix' config.mergedName ./name-merge-attrsWith-2.nix
|
||||
|
||||
# Even with multiple assignments, a type error should be thrown if any of them aren't valid
|
||||
checkConfigError 'A definition for option .* is not of type .*' \
|
||||
config.value ./declare-int-unsigned-value.nix ./define-value-list.nix ./define-value-int-positive.nix
|
||||
|
51
lib/tests/modules/name-merge-attrsWith-1.nix
Normal file
51
lib/tests/modules/name-merge-attrsWith-1.nix
Normal file
@ -0,0 +1,51 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) types mkOption;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
# Module A
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
options.mergedName = mkOption {
|
||||
default = { };
|
||||
type = types.attrsWith {
|
||||
placeholder = "id"; # <- This is beeing tested
|
||||
elemType = types.submodule {
|
||||
options.nested = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
# Module B
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
# defines the default placeholder "name"
|
||||
# type merging should resolve to "id"
|
||||
options.mergedName = mkOption {
|
||||
type = types.attrsOf (types.submodule { });
|
||||
};
|
||||
}
|
||||
)
|
||||
|
||||
# Output
|
||||
(
|
||||
{
|
||||
options,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.result = mkOption {
|
||||
default = lib.concatStringsSep "." (options.mergedName.type.getSubOptions options.mergedName.loc)
|
||||
.nested.loc;
|
||||
};
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
38
lib/tests/modules/name-merge-attrsWith-2.nix
Normal file
38
lib/tests/modules/name-merge-attrsWith-2.nix
Normal file
@ -0,0 +1,38 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) types mkOption;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
# Module A
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
options.mergedName = mkOption {
|
||||
default = { };
|
||||
type = types.attrsWith {
|
||||
placeholder = "id"; # <- this is beeing tested
|
||||
elemType = types.submodule {
|
||||
options.nested = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
# Module B
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
options.mergedName = mkOption {
|
||||
type = types.attrsWith {
|
||||
placeholder = "other"; # <- define placeholder = "other" (conflict)
|
||||
elemType = types.submodule { };
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
@ -608,17 +608,27 @@ rec {
|
||||
lhs.lazy
|
||||
else
|
||||
null;
|
||||
placeholder =
|
||||
if lhs.placeholder == rhs.placeholder then
|
||||
lhs.placeholder
|
||||
else if lhs.placeholder == "name" then
|
||||
rhs.placeholder
|
||||
else if rhs.placeholder == "name" then
|
||||
lhs.placeholder
|
||||
else
|
||||
null;
|
||||
in
|
||||
if elemType == null || lazy == null then
|
||||
if elemType == null || lazy == null || placeholder == null then
|
||||
null
|
||||
else
|
||||
{
|
||||
inherit elemType lazy;
|
||||
inherit elemType lazy placeholder;
|
||||
};
|
||||
in
|
||||
{
|
||||
elemType,
|
||||
lazy ? false,
|
||||
placeholder ? "name",
|
||||
}:
|
||||
mkOptionType {
|
||||
name = if lazy then "lazyAttrsOf" else "attrsOf";
|
||||
@ -645,16 +655,16 @@ rec {
|
||||
(pushPositions defs)))
|
||||
);
|
||||
emptyValue = { value = {}; };
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<${placeholder}>"]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: attrsWith { elemType = elemType.substSubModules m; inherit lazy; };
|
||||
substSubModules = m: attrsWith { elemType = elemType.substSubModules m; inherit lazy placeholder; };
|
||||
functor = defaultFunctor "attrsWith" // {
|
||||
wrappedDeprecationMessage = { loc }: lib.warn ''
|
||||
The deprecated `type.functor.wrapped` attribute of the option `${showOption loc}` is accessed, use `type.nestedTypes.elemType` instead.
|
||||
'' elemType;
|
||||
payload = {
|
||||
# Important!: Add new function attributes here in case of future changes
|
||||
inherit elemType lazy;
|
||||
inherit elemType lazy placeholder;
|
||||
};
|
||||
inherit binOp;
|
||||
};
|
||||
|
@ -399,7 +399,7 @@ Composed types are types that take a type as parameter. `listOf
|
||||
returned instead for the same `mkIf false` definition.
|
||||
:::
|
||||
|
||||
`types.attrsWith` { *`elemType`*, *`lazy`* ? false }
|
||||
`types.attrsWith` { *`elemType`*, *`lazy`* ? false, *`placeholder`* ? "name" }
|
||||
|
||||
: An attribute set of where all the values are of *`elemType`* type.
|
||||
|
||||
@ -411,10 +411,18 @@ Composed types are types that take a type as parameter. `listOf
|
||||
`lazy`
|
||||
: Determines whether the attribute set is lazily evaluated. See: `types.lazyAttrsOf`
|
||||
|
||||
`placeholder` (`String`, default: `name` )
|
||||
: Placeholder string in documentation for the attribute names.
|
||||
The default value `name` results in the placeholder `<name>`
|
||||
|
||||
**Behavior**
|
||||
|
||||
- `attrsWith { elemType = t; }` is equivalent to `attrsOf t`
|
||||
- `attrsWith { lazy = true; elemType = t; }` is equivalent to `lazyAttrsOf t`
|
||||
- `attrsWith { placeholder = "id"; elemType = t; }`
|
||||
|
||||
Displays the option as `foo.<id>` in the manual.
|
||||
|
||||
|
||||
`types.uniq` *`t`*
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user