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(core): `resolve.withAdapter` to collect custom results from resolve or change resolution. (#389)

authored by

Victor Borja and committed by
GitHub
509fc879 7e84abfd

+160 -34
+5 -4
docs/src/content/docs/guides/configure-aspects.mdx
··· 200 200 The following are default `meta.*` attributes defined by Den: 201 201 202 202 ```nix 203 - aspect.name # "den.aspects.igloo" 204 - meta.loc # the location [ "den" "aspects" "igloo" ] from where name is derived. 205 - meta.file # the first file where this aspect was defined. 206 - meta.self # a reference to the aspect module `config`. 203 + aspect.name # "igloo", the submodule name 204 + aspect.meta.loc # the location [ "den" "aspects" "igloo" ] from where name is derived. 205 + aspect.meta.name # "den.aspects.igloo" derived from loc 206 + aspect.meta.file # the last file where this aspect was defined. 207 + aspect.meta.self # a reference to the aspect module `config`. 207 208 ``` 208 209 209 210 You can acess meta values by referencing an aspect:
+1 -1
docs/src/content/docs/reference/lib.mdx
··· 146 146 147 147 ## `den.lib.aspects` 148 148 149 - Den aspects API. Provides `resolve`, and aspect type definitions. 149 + Den aspects API. Provides aspect type definitions, `resolve`, `resolve.withAdapter` and basic `adapters`
+52
nix/lib/aspects/adapters.nix
··· 1 + # Adapters for resolve.withAdapter. Default one is module. 2 + # 3 + # These adapters determine the return value of resolve. The adapters 4 + # are called by resolve for each resolved aspect, and the adapter can choose 5 + # to recurse or to replace which aspects will be used. 6 + # 7 + # Only basic adapters are provided here, see the arguments given by resolve.nix to them. 8 + # Some adapters compose by taking other adapters as parameters. 9 + { lib, ... }: 10 + let 11 + 12 + # Default adapter imports all classModules on a single module and recurses on includes unconditonally. 13 + module = 14 + { 15 + classModule, 16 + recurse, 17 + aspect, 18 + ... 19 + }: 20 + { 21 + imports = classModule ++ (lib.concatMap (i: (recurse i).imports or [ ]) (aspect.includes or [ ])); 22 + }; 23 + 24 + filter = 25 + pred: adapter: args: 26 + if pred args.aspect then adapter args else { }; 27 + 28 + # transforms the result of other adapters using f. 29 + map = 30 + f: adapter: args: 31 + f (adapter args); 32 + 33 + # transform each aspect into another by applying f to it. 34 + mapAspect = 35 + f: adapter: args: 36 + adapter (args // { aspect = f args.aspect; }); 37 + 38 + # transforms aspect includes by applying f to it. 39 + mapIncludes = 40 + f: adapter: args: 41 + adapter (args // { recurse = included: args.recurse (f included); }); 42 + 43 + in 44 + { 45 + inherit 46 + module 47 + filter 48 + map 49 + mapAspect 50 + mapIncludes 51 + ; 52 + }
+2 -1
nix/lib/aspects/default.nix
··· 5 5 }: 6 6 let 7 7 rawTypes = import ./types.nix { inherit den lib; }; 8 + adapters = import ./adapters.nix { inherit den lib; }; 8 9 resolve = import ./resolve.nix { inherit den lib; }; 9 10 10 11 defaultFunctor = (den.lib.parametric { }).__functor; ··· 12 13 types = lib.mapAttrs (_: v: v typesConf) rawTypes; 13 14 in 14 15 { 15 - inherit types resolve; 16 + inherit types adapters resolve; 16 17 }
+29 -12
nix/lib/aspects/resolve.nix
··· 2 2 let 3 3 4 4 inherit (den.lib) canTake take; 5 + inherit (den.lib.aspects) adapters; 5 6 6 7 apply = 7 8 provided: args: ··· 19 20 in 20 21 if lib.isFunction res then mod else res; 21 22 22 - resolve = 23 - class: prev-chain: provided: 23 + withAdapter = 24 + adapter: class: 24 25 let 25 - aspect = apply provided { inherit class aspect-chain; }; 26 + go = 27 + prevChain: provided: 28 + let 29 + aspect = apply provided { inherit class aspect-chain; }; 26 30 27 - aspect-chain = prev-chain ++ [ provided ] ++ (lib.optional (provided != aspect) aspect); 31 + aspect-chain = prevChain ++ [ provided ] ++ (lib.optional (provided != aspect) aspect); 28 32 29 - classModule = lib.optional (aspect ? ${class}) ( 30 - lib.setDefaultModuleLocation "${class}@${aspect.name}" aspect.${class} 31 - ); 33 + classModule = lib.optional (aspect ? ${class}) ( 34 + lib.setDefaultModuleLocation "${class}@${aspect.name}" aspect.${class} 35 + ); 32 36 33 - imports = classModule ++ (map (resolve class aspect-chain) (aspect.includes or [ ])); 37 + recurse = go aspect-chain; 38 + in 39 + adapter { 40 + inherit 41 + aspect 42 + class 43 + classModule 44 + recurse 45 + aspect-chain 46 + ; 47 + }; 34 48 in 35 - { 36 - inherit imports; 37 - }; 49 + go [ ]; 50 + 51 + resolve = withAdapter adapters.module; 38 52 39 53 in 40 - class: aspect: resolve class [ ] aspect 54 + { 55 + inherit withAdapter; 56 + __functor = _: resolve; 57 + }
+6 -6
nix/lib/aspects/types.nix
··· 32 32 loc: defs: 33 33 (aspectType cnf).merge loc [ 34 34 { 35 - file = (lib.head defs).file; 35 + file = (lib.last defs).file; 36 36 value = { 37 37 __functor = _: eth.merge loc defs; 38 38 }; ··· 55 55 defs 56 56 ++ [ 57 57 { 58 - file = (lib.head defs).file; 58 + file = (lib.last defs).file; 59 59 value = aspectMeta loc defs; 60 60 } 61 61 ] ··· 65 65 loc: defs: 66 66 { config, ... }: 67 67 let 68 - names = map (x: if builtins.isString x then x else "<anon>") loc; 68 + names = map (x: if builtins.isString x then x else "<anon>") config.meta.loc; 69 69 nameFromLoc = lib.concatStringsSep "." names; 70 70 in 71 71 { 72 - name = nameFromLoc; 73 - meta.file = (lib.last defs).file; 74 - meta.loc = loc; 72 + meta.name = lib.mkForce nameFromLoc; 73 + meta.file = lib.mkForce (lib.last defs).file; 74 + meta.loc = lib.mkForce loc; 75 75 }; 76 76 77 77 aspectSubmodule =
+4 -4
templates/ci/modules/features/aspect-meta.nix
··· 34 34 { den, funnyNames, ... }: 35 35 { 36 36 den.aspects.foo = { 37 - funny.names = [ den.aspects.foo.name ]; 37 + funny.names = [ den.aspects.foo.meta.name ]; 38 38 }; 39 39 40 40 expr = funnyNames den.aspects.foo; ··· 48 48 den.aspects.foo = 49 49 { config, ... }: 50 50 { 51 - funny.names = [ config.name ]; 51 + funny.names = [ config.meta.name ]; 52 52 }; 53 53 54 54 expr = funnyNames den.aspects.foo; ··· 80 80 KEYS 81 81 ; 82 82 }; 83 - expected.KEYS = "file:loc:self"; 83 + expected.KEYS = "file:loc:name:self"; 84 84 } 85 85 ); 86 86 ··· 110 110 KEYS 111 111 ; 112 112 }; 113 - expected.KEYS = "file:foo:loc:self"; 113 + expected.KEYS = "file:foo:loc:name:self"; 114 114 } 115 115 ); 116 116
+6 -6
templates/ci/modules/features/perUser-perHost.nix
··· 54 54 )) 55 55 ]; 56 56 57 - expr = lib.sort (a: b: a < b) igloo.funny; 57 + expr = lib.sort lib.lessThan igloo.funny; 58 58 expected = [ 59 59 "atHost perHost igloo fun" 60 60 "atHost perHost static" ··· 118 118 )) 119 119 ]; 120 120 121 - expr = igloo.funny; 121 + expr = lib.sort lib.lessThan igloo.funny; 122 122 expected = [ 123 123 "atHost perUser pingu@igloo fun" 124 - "atHost perUser static" # pingu 124 + "atHost perUser static" 125 + "atHost perUser static" 125 126 "atHost perUser tux@igloo fun" 126 - "atHost perUser static" # tux 127 - "atUser perUser tux@igloo fun" 128 127 "atUser perUser static" 128 + "atUser perUser tux@igloo fun" 129 129 ]; 130 130 } 131 131 ); ··· 170 170 )) 171 171 ]; 172 172 173 - expr = lib.sort (a: b: a < b) config.flake.homeConfigurations.tux.config.funny; 173 + expr = lib.sort lib.lessThan config.flake.homeConfigurations.tux.config.funny; 174 174 expected = [ 175 175 "atHome perHome static" 176 176 "atHome perHome tux fun"
+55
templates/ci/modules/features/resolve-adapters.nix
··· 1 + { denTest, lib, ... }: 2 + { 3 + flake.tests.resolve-adapters = 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-basic-trace-includes = denTest ( 14 + { den, lib, ... }: 15 + { 16 + 17 + den.aspects.foo.includes = [ den.aspects.bar ]; 18 + den.aspects.bar.includes = [ den.aspects.baz ]; 19 + den.aspects.baz.nixos = { }; 20 + 21 + expr = den.lib.aspects.resolve.withAdapter traceName "nixos" den.aspects.foo; 22 + expected.trace = [ 23 + "foo" 24 + [ 25 + "bar" 26 + [ "baz" ] 27 + ] 28 + ]; 29 + } 30 + ); 31 + 32 + test-filter-compose-with-trace-includes = denTest ( 33 + { den, lib, ... }: 34 + { 35 + 36 + den.aspects.foo.includes = [ den.aspects.bar ]; 37 + den.aspects.bar.includes = [ den.aspects.baz ]; 38 + den.aspects.baz.nixos = { }; 39 + 40 + expr = 41 + let 42 + inherit (den.lib.aspects) resolve adapters; 43 + notBar = adapters.filter (aspect: aspect.name != "bar"); 44 + composed = notBar traceName; 45 + in 46 + resolve.withAdapter composed "nixos" den.aspects.foo; 47 + expected.trace = [ 48 + "foo" 49 + [ ] 50 + ]; 51 + } 52 + ); 53 + 54 + }; 55 + }