diff --git a/lib/introspection/default.nix b/lib/introspection/default.nix index a3cef0f6f..a7aebeafe 100644 --- a/lib/introspection/default.nix +++ b/lib/introspection/default.nix @@ -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) diff --git a/lib/introspection/test.nix b/lib/introspection/test.nix index f04ca51a0..36fe58e6c 100644 --- a/lib/introspection/test.nix +++ b/lib/introspection/test.nix @@ -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 = [ "" ]; + files = [ + "" + "" + ]; prio = 100; total = false; }; nested = { + __this = { + files = [ "" ]; + prio = 50; + total = true; + }; bar = { __this = { files = [ "" ]; @@ -344,10 +357,15 @@ in }; }; other = { + __this = { + files = [ "" ]; + prio = 50; + total = true; + }; bar = { __this = { files = [ "" ]; - prio = 50; + prio = 100; total = false; }; }; @@ -401,7 +419,17 @@ in total = false; }; a = { + __this = { + files = [ "" ]; + prio = 100; + total = false; + }; b = { + __this = { + files = [ "" ]; + prio = 100; + total = true; + }; bar = { __this = { files = [ "" ]; @@ -411,6 +439,11 @@ in }; }; c = { + __this = { + files = [ "" ]; + prio = 100; + total = true; + }; bar = { __this = { files = [ "" ]; @@ -421,7 +454,17 @@ in }; }; x = { + __this = { + files = [ "" ]; + prio = 100; + total = false; + }; y = { + __this = { + files = [ "" ]; + prio = 100; + total = true; + }; bar = { __this = { files = [ "" ]; @@ -431,6 +474,11 @@ in }; }; z = { + __this = { + files = [ "" ]; + prio = 100; + total = true; + }; bar = { __this = { files = [ "" ]; @@ -443,7 +491,6 @@ in }; }; }; - test_attrsOf_submodule_default = let evaluated = eval [ @@ -496,6 +543,14 @@ in total = false; }; jon = { + __this = { + files = [ + "" + "inventory.json" + ]; + prio = 100; + total = true; + }; fludl = { __this = { files = [ "" ];