mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-06-05 21:03:40 +00:00
types.attrListWith: add asAttrs
This allows the type's return value to be accessed more easily.
Motivating use case:
- Built-in module provides CLI functionality by declaring
an `attrListWith { asAttrs = true; }`, extracting the ordered list
from `valueMeta` for the purpose of creating the `argv`.
- User modules can read the command line's flags directly without
having to parse the list of attrs.
This commit is contained in:
committed by
Johannes Kirschbauer
parent
17fdb6f68a
commit
f1b62fdc4e
@@ -42,6 +42,27 @@ in
|
||||
);
|
||||
};
|
||||
|
||||
# asAttrs: value is a merged attrset, ordered list in valueMeta
|
||||
asAttrs = mkOption {
|
||||
type = types.lazyAttrsOf (
|
||||
types.attrListWith {
|
||||
elemType = types.str;
|
||||
asAttrs = true;
|
||||
mergeAttrValues = _name: values: lib.last values;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# asAttrs with default mergeAttrValues: duplicates collected into lists
|
||||
asAttrsDefault = mkOption {
|
||||
type = types.lazyAttrsOf (
|
||||
types.attrListWith {
|
||||
elemType = types.int;
|
||||
asAttrs = true;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# Strict wrappers that force deep evaluation, for testing error cases
|
||||
attrListStrict = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
@@ -381,6 +402,53 @@ in
|
||||
}
|
||||
];
|
||||
|
||||
# asAttrs: unique keys — value is a plain attrset
|
||||
asAttrs.unique = [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# asAttrs: duplicate keys — last in order wins
|
||||
asAttrs.duplicateKeys = mkMerge [
|
||||
{ x = mkOrder 500 "first"; }
|
||||
{ x = mkOrder 1500 "last"; }
|
||||
{ y = "only"; }
|
||||
];
|
||||
|
||||
# asAttrs: with ordering — value is attrset, ordered list in valueMeta
|
||||
asAttrs.ordered = {
|
||||
z = mkOrder 200 "z-val";
|
||||
a = mkOrder 100 "a-val";
|
||||
};
|
||||
|
||||
# asAttrs: with mkForce — forced key overrides
|
||||
asAttrs.withForce = mkMerge [
|
||||
{ x = "unused: overridden by mkForce"; }
|
||||
{
|
||||
x = mkForce "forced";
|
||||
y = "kept";
|
||||
}
|
||||
];
|
||||
|
||||
# asAttrs: empty
|
||||
asAttrs.empty = [ ];
|
||||
|
||||
# asAttrsDefault: unique keys
|
||||
asAttrsDefault.unique = [
|
||||
{ a = 1; }
|
||||
{ b = 2; }
|
||||
];
|
||||
|
||||
# asAttrsDefault: duplicate keys — default collects into lists
|
||||
asAttrsDefault.duplicates = mkMerge [
|
||||
{ x = mkOrder 500 10; }
|
||||
{ x = mkOrder 1500 30; }
|
||||
{ y = 99; }
|
||||
[
|
||||
{ x = 20; }
|
||||
]
|
||||
];
|
||||
|
||||
# either: attrList branch matches for list input
|
||||
eitherAttrListOrInt = [
|
||||
{ a = "hello"; }
|
||||
@@ -723,6 +791,79 @@ in
|
||||
{ a = "hello"; }
|
||||
];
|
||||
|
||||
# asAttrs: unique keys — value is a plain attrset
|
||||
assert
|
||||
cfg.asAttrs.unique == {
|
||||
a = "alpha";
|
||||
b = "beta";
|
||||
};
|
||||
# ordered list preserved in valueMeta
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.unique.attrListValue == [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# asAttrs: duplicate keys — last in order wins
|
||||
assert
|
||||
cfg.asAttrs.duplicateKeys == {
|
||||
x = "last";
|
||||
y = "only";
|
||||
};
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.duplicateKeys.attrListValue == [
|
||||
{ x = "first"; }
|
||||
{ y = "only"; }
|
||||
{ x = "last"; }
|
||||
];
|
||||
|
||||
# asAttrs: ordered — value is attrset (unordered), list in valueMeta preserves order
|
||||
assert
|
||||
cfg.asAttrs.ordered == {
|
||||
a = "a-val";
|
||||
z = "z-val";
|
||||
};
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.ordered.attrListValue == [
|
||||
{ a = "a-val"; }
|
||||
{ z = "z-val"; }
|
||||
];
|
||||
|
||||
# asAttrs: mkForce — forced key overrides, value is attrset
|
||||
assert
|
||||
cfg.asAttrs.withForce == {
|
||||
x = "forced";
|
||||
y = "kept";
|
||||
};
|
||||
|
||||
# asAttrs: empty — value is empty attrset
|
||||
assert cfg.asAttrs.empty == { };
|
||||
|
||||
# asAttrsDefault: unique keys — each value wrapped in singleton list
|
||||
assert
|
||||
cfg.asAttrsDefault.unique == {
|
||||
a = [ 1 ];
|
||||
b = [ 2 ];
|
||||
};
|
||||
|
||||
# asAttrsDefault: duplicate keys — values collected into list in order
|
||||
assert
|
||||
cfg.asAttrsDefault.duplicates == {
|
||||
x = [
|
||||
10
|
||||
20
|
||||
30
|
||||
];
|
||||
y = [ 99 ];
|
||||
};
|
||||
assert
|
||||
c.options.asAttrsDefault.valueMeta.attrs.duplicates.attrListValue == [
|
||||
{ x = 10; }
|
||||
{ y = 99; }
|
||||
{ x = 20; }
|
||||
{ x = 30; }
|
||||
];
|
||||
|
||||
# Error cases are tested via checkConfigError in modules.sh
|
||||
|
||||
"ok";
|
||||
|
||||
@@ -813,7 +813,11 @@ rec {
|
||||
attrListOf = elemType: attrListWith { inherit elemType; };
|
||||
|
||||
attrListWith =
|
||||
{ elemType }:
|
||||
{
|
||||
elemType,
|
||||
asAttrs ? false,
|
||||
mergeAttrValues ? _name: values: values,
|
||||
}:
|
||||
mkOptionType rec {
|
||||
name = "attrListOf";
|
||||
description = "attribute list of ${
|
||||
@@ -932,21 +936,37 @@ rec {
|
||||
];
|
||||
}) items
|
||||
);
|
||||
|
||||
attrListValue = map (e: { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; }) evals;
|
||||
in
|
||||
{
|
||||
headError = checkDefsForError check loc defs;
|
||||
value = map (e: { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; }) evals;
|
||||
value = if asAttrs then zipAttrsWith mergeAttrValues attrListValue else attrListValue;
|
||||
valueMeta.attrList = map (e: e.eval.checkedAndMerged.valueMeta) evals;
|
||||
/**
|
||||
The ordered list representation, especially useful when asAttrs is set.
|
||||
*/
|
||||
valueMeta.attrListValue = attrListValue;
|
||||
};
|
||||
};
|
||||
emptyValue = {
|
||||
value = [ ];
|
||||
value = if asAttrs then { } else [ ];
|
||||
};
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: attrListOf (elemType.substSubModules m);
|
||||
substSubModules =
|
||||
m:
|
||||
attrListWith {
|
||||
inherit asAttrs mergeAttrValues;
|
||||
elemType = elemType.substSubModules m;
|
||||
};
|
||||
functor = elemTypeFunctor name { inherit elemType; } // {
|
||||
type = payload: types.attrListOf payload.elemType;
|
||||
type =
|
||||
payload:
|
||||
types.attrListWith {
|
||||
inherit asAttrs mergeAttrValues;
|
||||
inherit (payload) elemType;
|
||||
};
|
||||
};
|
||||
nestedTypes.elemType = elemType;
|
||||
};
|
||||
|
||||
@@ -512,7 +512,7 @@ Composed types are types that take a type as parameter. `listOf
|
||||
Multiple definitions of the same option are concatenated and then sorted by priority.
|
||||
Entries at the same priority level preserve their definition order.
|
||||
|
||||
`types.attrListWith` { *`elemType`* }
|
||||
`types.attrListWith` { *`elemType`*, *`asAttrs`* ? false, *`mergeAttrValues`* ? _name: values: values }
|
||||
|
||||
: An ordered list of single-attribute attribute sets, where each value is of *`elemType`* type.
|
||||
|
||||
@@ -521,6 +521,17 @@ Composed types are types that take a type as parameter. `listOf
|
||||
`elemType` (Required)
|
||||
: Specifies the type of each value in the attribute list.
|
||||
|
||||
`asAttrs`
|
||||
: When `true`, the option value is an attribute set instead of a list.
|
||||
Duplicate keys are merged using `mergeAttrValues`.
|
||||
The ordered list is always available via `valueMeta.attrListValue`.
|
||||
|
||||
`mergeAttrValues`
|
||||
: A function `name: values: mergedValue` that controls how duplicate keys
|
||||
are combined when `asAttrs = true`. This is passed as the callback to
|
||||
`lib.zipAttrsWith`. The `values` list is in order of priority.
|
||||
By default, all values are collected into a list.
|
||||
|
||||
**Behavior**
|
||||
|
||||
- `attrListWith { elemType = t; }` is equivalent to `attrListOf t`
|
||||
|
||||
Reference in New Issue
Block a user