Merge master into staging-next

This commit is contained in:
github-actions[bot]
2024-12-09 18:05:05 +00:00
committed by GitHub
92 changed files with 2149 additions and 12646 deletions

View File

@@ -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);

View File

@@ -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 = [ {} ];

View File

@@ -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

View 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;
};
}
)
];
}

View 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 { };
};
};
}
)
];
}

View File

@@ -0,0 +1,15 @@
{ lib, ... }:
{
imports = [
{
options.sm = lib.mkOption {
type = lib.types.strMatching "\(.*\)";
};
}
{
options.sm = lib.mkOption {
type = lib.types.strMatching "\(.*\)";
};
}
];
}

View File

@@ -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`.