Merge master into staging-next
This commit is contained in:
@@ -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 = [ {} ];
|
||||
|
||||
@@ -190,6 +190,9 @@ checkConfigOutput '^420$' config.bare-submodule.deep ./declare-bare-submodule.ni
|
||||
checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./define-shorthandOnlyDefinesConfig-true.nix
|
||||
checkConfigError 'The option .bare-submodule.deep. in .*/declare-bare-submodule-deep-option.nix. is already declared in .*/declare-bare-submodule-deep-option-duplicate.nix' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./declare-bare-submodule-deep-option-duplicate.nix
|
||||
|
||||
# Check that strMatching can be merged
|
||||
checkConfigOutput '^"strMatching.*"$' options.sm.type.name ./strMatching-merge.nix
|
||||
|
||||
# Check integer types.
|
||||
# unsigned
|
||||
checkConfigOutput '^42$' config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix
|
||||
@@ -391,6 +394,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 { };
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
15
lib/tests/modules/strMatching-merge.nix
Normal file
15
lib/tests/modules/strMatching-merge.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
{
|
||||
options.sm = lib.mkOption {
|
||||
type = lib.types.strMatching "\(.*\)";
|
||||
};
|
||||
}
|
||||
{
|
||||
options.sm = lib.mkOption {
|
||||
type = lib.types.strMatching "\(.*\)";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -461,6 +461,11 @@ rec {
|
||||
descriptionClass = "noun";
|
||||
check = x: str.check x && builtins.match pattern x != null;
|
||||
inherit (str) merge;
|
||||
functor = defaultFunctor "strMatching" // {
|
||||
type = payload: strMatching payload.pattern;
|
||||
payload = { inherit pattern; };
|
||||
binOp = lhs: rhs: if lhs == rhs then lhs else null;
|
||||
};
|
||||
};
|
||||
|
||||
# Merge multiple definitions by concatenating them (with the given
|
||||
@@ -475,9 +480,10 @@ rec {
|
||||
check = isString;
|
||||
merge = loc: defs: concatStringsSep sep (getValues defs);
|
||||
functor = (defaultFunctor name) // {
|
||||
payload = sep;
|
||||
binOp = sepLhs: sepRhs:
|
||||
if sepLhs == sepRhs then sepLhs
|
||||
payload = { inherit sep; };
|
||||
type = payload: types.separatedString payload.sep;
|
||||
binOp = lhs: rhs:
|
||||
if lhs.sep == rhs.sep then { inherit (lhs) sep; }
|
||||
else null;
|
||||
};
|
||||
};
|
||||
@@ -608,17 +614,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 +661,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;
|
||||
};
|
||||
@@ -1014,7 +1030,11 @@ rec {
|
||||
else "conjunction";
|
||||
check = flip elem values;
|
||||
merge = mergeEqualOption;
|
||||
functor = (defaultFunctor name) // { payload = values; binOp = a: b: unique (a ++ b); };
|
||||
functor = (defaultFunctor name) // {
|
||||
payload = { inherit values; };
|
||||
type = payload: types.enum payload.values;
|
||||
binOp = a: b: { values = unique (a.values ++ b.values); };
|
||||
};
|
||||
};
|
||||
|
||||
# Either value of type `t1` or `t2`.
|
||||
|
||||
Reference in New Issue
Block a user