Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: per-aspect adapter accumulation in resolve (#397)

Aspects can declare meta.adapter (typed nullOr functionTo raw) to
compose into resolution for their subtree. resolve's go function
accumulates aspect-level adapters as it descends, composing them with
the inherited adapter.

authored by

Jason Bowman and committed by
GitHub
a2e7241a 6dc322d1

+150 -5
+45
nix/lib/aspects/adapters.nix
··· 40 40 f: adapter: args: 41 41 adapter (args // { recurse = included: args.recurse (f included); }); 42 42 43 + # Handles per-aspect adapter accumulation via meta.adapter. 44 + # Composes meta.adapter with the inner adapter, removes includes that 45 + # would resolve to { }, and tags survivors for downstream propagation. 46 + filterIncludes = 47 + inner: 48 + args@{ aspect, resolveChild, ... }: 49 + let 50 + metaAdapter = aspect.meta.adapter or null; 51 + in 52 + if metaAdapter != null && aspect ? includes then 53 + let 54 + composed = metaAdapter (filterIncludes inner); 55 + keeps = 56 + i: 57 + composed ( 58 + args 59 + // { 60 + aspect = resolveChild i; 61 + classModule = [ ]; 62 + } 63 + ) != { }; 64 + tag = 65 + i: 66 + if builtins.isAttrs i && i.meta.adapter or null == null then 67 + i 68 + // { 69 + meta = (i.meta or { }) // { 70 + adapter = metaAdapter; 71 + }; 72 + } 73 + else 74 + i; 75 + in 76 + inner ( 77 + args 78 + // { 79 + aspect = aspect // { 80 + includes = builtins.map tag (lib.filter keeps aspect.includes); 81 + }; 82 + } 83 + ) 84 + else 85 + inner args; 86 + 43 87 in 44 88 { 45 89 inherit ··· 48 92 map 49 93 mapAspect 50 94 mapIncludes 95 + filterIncludes 51 96 ; 52 97 }
+10 -2
nix/lib/aspects/resolve.nix
··· 31 31 aspect-chain = prevChain ++ [ provided ] ++ (lib.optional (provided != aspect) aspect); 32 32 33 33 classModule = lib.optional (aspect ? ${class}) ( 34 - lib.setDefaultModuleLocation "${class}@${aspect.name}" aspect.${class} 34 + lib.setDefaultModuleLocation "${class}@${aspect.name or "<anon>"}" aspect.${class} 35 35 ); 36 36 37 37 recurse = go aspect-chain; 38 + 39 + resolveChild = 40 + i: 41 + apply i { 42 + inherit class; 43 + aspect-chain = aspect-chain ++ [ i ]; 44 + }; 38 45 in 39 46 adapter { 40 47 inherit ··· 43 50 classModule 44 51 recurse 45 52 aspect-chain 53 + resolveChild 46 54 ; 47 55 }; 48 56 in 49 57 go [ ]; 50 58 51 - resolve = withAdapter adapters.module; 59 + resolve = withAdapter (adapters.filterIncludes adapters.module); 52 60 53 61 in 54 62 {
+6 -1
nix/lib/aspects/types.nix
··· 101 101 description = "Aspect attached meta data"; 102 102 type = lib.types.submodule { 103 103 freeformType = lib.types.lazyAttrsOf lib.types.unspecified; 104 - self = config; 104 + config.self = config; 105 + options.adapter = lib.mkOption { 106 + description = "Adapter to compose into resolution for this aspect's subtree"; 107 + type = lib.types.nullOr (lastFunctionTo lib.types.raw); 108 + default = null; 109 + }; 105 110 }; 106 111 defaultText = lib.literalExpression "{ }"; 107 112 default = { };
+87
templates/ci/modules/features/aspect-adapter.nix
··· 1 + { denTest, lib, ... }: 2 + { 3 + flake.tests.aspect-adapter = 4 + let 5 + traceName = 6 + { aspect, recurse, ... }: 7 + { 8 + trace = [ aspect.name ] ++ map (i: (recurse i).trace or [ ]) (aspect.includes or [ ]); 9 + }; 10 + in 11 + { 12 + 13 + test-meta-adapter-filters-subtree = denTest ( 14 + { den, ... }: 15 + { 16 + den.aspects.foo.includes = [ 17 + den.aspects.bar 18 + den.aspects.baz 19 + ]; 20 + den.aspects.foo.meta.adapter = 21 + inherited: den.lib.aspects.adapters.filter (a: a.name != "baz") inherited; 22 + den.aspects.bar.nixos = { }; 23 + den.aspects.baz.nixos = { }; 24 + 25 + expr = 26 + den.lib.aspects.resolve.withAdapter (den.lib.aspects.adapters.filterIncludes traceName) "nixos" 27 + den.aspects.foo; 28 + expected.trace = [ 29 + "foo" 30 + [ "bar" ] 31 + ]; 32 + } 33 + ); 34 + 35 + test-meta-adapter-only-affects-subtree = denTest ( 36 + { den, ... }: 37 + { 38 + den.aspects.root.includes = [ 39 + den.aspects.foo 40 + den.aspects.baz 41 + ]; 42 + den.aspects.foo.includes = [ den.aspects.bar ]; 43 + den.aspects.foo.meta.adapter = 44 + inherited: den.lib.aspects.adapters.filter (a: a.name != "baz") inherited; 45 + den.aspects.bar.nixos = { }; 46 + den.aspects.baz.nixos = { }; 47 + 48 + expr = 49 + den.lib.aspects.resolve.withAdapter (den.lib.aspects.adapters.filterIncludes traceName) "nixos" 50 + den.aspects.root; 51 + expected.trace = [ 52 + "root" 53 + [ 54 + "foo" 55 + [ "bar" ] 56 + ] 57 + [ "baz" ] 58 + ]; 59 + } 60 + ); 61 + 62 + test-meta-adapter-composes-with-caller = denTest ( 63 + { den, ... }: 64 + { 65 + den.aspects.foo.includes = [ 66 + den.aspects.bar 67 + den.aspects.baz 68 + ]; 69 + den.aspects.foo.meta.adapter = 70 + inherited: den.lib.aspects.adapters.filter (a: a.name != "bar") inherited; 71 + den.aspects.bar.nixos = { }; 72 + den.aspects.baz.nixos = { }; 73 + 74 + expr = 75 + let 76 + outerAdapter = den.lib.aspects.adapters.filter (a: a.name != "baz") traceName; 77 + in 78 + den.lib.aspects.resolve.withAdapter (den.lib.aspects.adapters.filterIncludes outerAdapter) "nixos" 79 + den.aspects.foo; 80 + expected.trace = [ 81 + "foo" 82 + ]; 83 + } 84 + ); 85 + 86 + }; 87 + }
+2 -2
templates/ci/modules/features/aspect-meta.nix
··· 80 80 KEYS 81 81 ; 82 82 }; 83 - expected.KEYS = "file:loc:name:self"; 83 + expected.KEYS = "adapter:file:loc:name:self"; 84 84 } 85 85 ); 86 86 ··· 110 110 KEYS 111 111 ; 112 112 }; 113 - expected.KEYS = "file:foo:loc:name:self"; 113 + expected.KEYS = "adapter:file:foo:loc:name:self"; 114 114 } 115 115 ); 116 116