attrListWith
lib.types.attrListWith
Docs pulled from | This Revision | 15 minutes ago
Contribute
Enhance the ecosystem with your expertise! Contribute to fill the gaps in documentation. Your input can make a difference.
Noogle detected
Implementation
The following is the current implementation of this function.
{
elemType,
asAttrs ? false,
mergeAttrValues ? _name: values: values,
}:
mkOptionType rec {
name = "attrListOf";
description = "attribute list of ${
optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType
}";
descriptionClass = "composite";
check = {
__functor = _self: x: isList x || isAttrs x;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
(self.v2 { inherit loc defs; }).value;
v2 =
{ loc, defs }:
let
# Peel order and override properties from a value in any nesting order.
# Returns { value, prio, overridePrio }.
# mkOrder is stripped (we consume it for sorting).
# mkOverride is preserved in value (mergeDefinitions strips it).
peelProperties =
value:
let
type = value._type or null;
in
if type == "order" then
let
inner = peelProperties value.content;
in
{
inherit (inner) value overridePrio;
prio = value.priority;
}
else if type == "override" then
let
inner = peelProperties value.content;
in
{
inherit (inner) prio;
overridePrio = value.priority;
# Re-wrap mkOverride around the inner value (with mkOrder stripped)
value = mkOverride value.priority inner.value;
}
else
{
inherit value;
prio = defaultOrderPriority;
overridePrio = defaultOverridePriority;
};
# Extract { file, key, value, prio, overridePrio } from a single-key attrset,
# optionally wrapped in mkOrder at the element level (list format).
extractItem =
file: raw:
let
hasOrder = isType "order" raw;
item = if hasOrder then raw.content else raw;
key = head (attrNames item);
peeled = peelProperties item.${key};
in
if isAttrs item && length (attrNames item) == 1 then
peeled
// {
inherit file key;
prio = if hasOrder then raw.priority else peeled.prio;
}
else
throw "A definition for option `${showOption loc}' is not of type `${description}'. ${
if !isAttrs item then
"Each list element must be an attribute set, but got ${builtins.typeOf item}"
else
"Each list element must be a single-key attribute set, but got ${toString (length (attrNames item))} keys"
}.${
showDefs [
{
inherit file;
value = raw;
}
]
}";
# Convert a definition to a flat list of { file, key, value, prio, overridePrio }
defToItems =
def:
if isList def.value then
map (extractItem def.file) def.value
else
# isAttrs: properties are on the values directly
map (
key:
peelProperties def.value.${key}
// {
inherit (def) file;
inherit key;
}
) (attrNames def.value);
allItems = concatMap defToItems defs;
# Per key, find the highest override priority (lowest number)
winningOverridePrio = foldl' (
acc: item:
let
prev = acc.${item.key} or defaultOverridePriority;
in
if item.overridePrio < prev then
acc // { ${item.key} = item.overridePrio; }
else
# minimize `//` operations
acc
) { } allItems;
# Keep only items at the winning override priority for their key
items = sort (a: b: a.prio < b.prio) (
filter (
item: item.overridePrio == winningOverridePrio.${item.key} or defaultOverridePriority
) allItems
);
evals = filter (e: e.eval.optionalValue ? value) (
map (item: {
inherit (item) key file prio;
eval = mergeDefinitions (loc ++ [ item.key ]) elemType [
{
inherit (item) file value;
}
];
}) items
);
attrListValue = map (e: { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; }) evals;
in
{
headError = checkDefsForError check loc defs;
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;
valueMeta.definitions = map (
e:
mkDefinition {
inherit (e) file;
value = mkOrder e.prio { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; };
}
) evals;
};
};
emptyValue = {
value = if asAttrs then { } else [ ];
};
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]);
getSubModules = elemType.getSubModules;
substSubModules =
m:
attrListWith {
inherit asAttrs mergeAttrValues;
elemType = elemType.substSubModules m;
};
typeMerge = t: null; # Disable type merging
nestedTypes.elemType = elemType;
}