lib/introspect: recurse for nested attrsOf

This commit is contained in:
Johannes Kirschbauer
2025-10-01 16:56:43 +02:00
parent a8156d2fa6
commit 1a8131f17f
2 changed files with 127 additions and 20 deletions

View File

@@ -10,6 +10,46 @@ let
]
);
pushPositions = map (
def:
lib.mapAttrs (_n: v: {
inherit (def) file;
value = v;
}) def.value
);
unwrapNullOr =
type:
let
typeName = type.name or null;
in
if typeName == "nullOr" then type.nestedTypes.name or null else typeName;
mergeAttrs =
{ type, definitionsWithLocations }:
let
# Vendored merge from lib.types.attrsOf
# Because we still cannot access highest prio for the individual attrs yet.
elemType = type.nestedTypes.elemType;
mergedAttrs = lib.zipAttrsWith (name: defs: lib.modules.mergeDefinitions ([ name ]) elemType defs) (
pushPositions definitionsWithLocations
);
headType = unwrapNullOr elemType;
nullable = elemType.name or null == "nullOr";
total = elemType.name or null == "submodule";
in
lib.mapAttrs (_name: merged: {
__this = {
prio = merged.defsFinal'.highestPrio;
files = map (def: def.file) merged.defsFinal'.values;
inherit
headType
nullable
total
;
};
}) mergedAttrs;
/**
Takes a set of options as returned by `configuration`
@@ -45,19 +85,15 @@ let
lib.mapAttrs (
_: opt:
let
headType =
let
typeName = opt.type.name or null;
in
if typeName == "nullOr" then opt.type.nestedTypes.name or null else typeName;
headType = unwrapNullOr opt.type;
nullable = opt.type.name or null == "nullOr";
total = opt.type.name or null == "submodule";
definitionInfo = {
__this = {
prio = opt.highestPrio or null;
files = opt.files or [ ];
inherit headType nullable;
total = opt.type.name or null == "submodule";
inherit headType nullable total;
};
};
@@ -71,18 +107,34 @@ let
/**
Maps attrsOf and lazyAttrsOf
*/
handleAttrsOf = attrs: lib.mapAttrs (_: handleMeta) attrs;
handleAttrsOf =
type: defs: attrs:
lib.mapAttrs (
name: meta:
(mergeAttrs {
inherit type;
definitionsWithLocations = defs;
}).${name}
// handleMeta {
inherit meta;
definitionsWithLocations =
(builtins.zipAttrsWith (_name: values: values) (pushPositions defs)).${name};
}
) attrs;
/**
Maps attrsOf and lazyAttrsOf
*/
handleListOf = list: { __list = lib.map handleMeta list; };
handleListOf = list: { __list = lib.map handleMeta { meta = list; }; };
/**
Unwraps the valueMeta of an option based on its type
*/
handleMeta =
meta:
{
meta,
definitionsWithLocations ? [ ],
}:
let
hasType = meta ? _internal.type;
type = meta._internal.type;
@@ -93,7 +145,7 @@ let
# TODO: handle types
getPrios { options = filterOptions meta.configuration.options; }
else if type.name == "attrsOf" || type.name == "lazyAttrsOf" then
handleAttrsOf meta.attrs
handleAttrsOf meta._internal.type definitionsWithLocations meta.attrs
# TODO: Add index support in nixpkgs first
# else if type.name == "listOf" then
# handleListOf meta.list
@@ -103,7 +155,7 @@ let
if opt ? type && opt.type.name == "submodule" then
(definitionInfo) // submodulePrios
else if opt ? type && (opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf") then
definitionInfo // (handleAttrsOf opt.valueMeta.attrs)
definitionInfo // (handleAttrsOf opt.type opt.definitionsWithLocations opt.valueMeta.attrs)
# TODO: Add index support in nixpkgs, otherwise we cannot
else if opt ? type && (opt.type.name == "listOf") then
definitionInfo // (handleListOf opt.valueMeta.list)

View File

@@ -314,12 +314,17 @@ in
}
);
};
}
{
config.foo.nested = lib.mkForce {
# <- 50 prio
"bar" = 2;
};
}
{
config.foo = {
"nested" = {
"bar" = 2; # <- 100 prio ?
};
"other" = {
"bar" = lib.mkForce 2; # <- 50 prio ?
"other" = lib.mkForce {
"bar" = 2; # <- 50 prio
};
};
}
@@ -330,11 +335,19 @@ in
expected = {
foo = {
__this = {
files = [ "<unknown-file>" ];
files = [
"<unknown-file>"
"<unknown-file>"
];
prio = 100;
total = false;
};
nested = {
__this = {
files = [ "<unknown-file>" ];
prio = 50;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
@@ -344,10 +357,15 @@ in
};
};
other = {
__this = {
files = [ "<unknown-file>" ];
prio = 50;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
prio = 50;
prio = 100;
total = false;
};
};
@@ -401,7 +419,17 @@ in
total = false;
};
a = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = false;
};
b = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
@@ -411,6 +439,11 @@ in
};
};
c = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
@@ -421,7 +454,17 @@ in
};
};
x = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = false;
};
y = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
@@ -431,6 +474,11 @@ in
};
};
z = {
__this = {
files = [ "<unknown-file>" ];
prio = 100;
total = true;
};
bar = {
__this = {
files = [ "<unknown-file>" ];
@@ -443,7 +491,6 @@ in
};
};
};
test_attrsOf_submodule_default =
let
evaluated = eval [
@@ -496,6 +543,14 @@ in
total = false;
};
jon = {
__this = {
files = [
"<unknown-file>"
"inventory.json"
];
prio = 100;
total = true;
};
fludl = {
__this = {
files = [ "<unknown-file>" ];