fake.modules transposition for aspect-oriented Dendritic Nix. with cross-aspect dependencies. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ dendrix.oeiuwq.com/Dendritic.html
dendritic nix aspect oriented

testing

+146 -66
+55
checkmate/modules/tests/aspect_assignment.nix
··· 1 + { lib, targetLib, ... }: 2 + { 3 + 4 + flake.tests."test-assign-aspects-on-scopes" = 5 + let 6 + flake-aspects-lib = import targetLib lib; 7 + 8 + first = lib.evalModules { 9 + modules = [ 10 + (flake-aspects-lib.new-scope "foo") 11 + (flake-aspects-lib.new-scope "bar") 12 + (flake-aspects-lib.new-scope "baz") 13 + { 14 + foo.aspects.a._.b._.c.nixos.x = [ "foo" ]; 15 + } 16 + { 17 + bar.aspects.a._.b._.c.nixos.x = [ "bar" ]; 18 + } 19 + { 20 + baz.aspects.a._.b._.c.nixos.x = [ "baz" ]; 21 + } 22 + ( 23 + { config, ... }: 24 + { 25 + bar = config.foo; 26 + } 27 + ) 28 + ( 29 + { config, ... }: 30 + { 31 + baz = config.bar; 32 + } 33 + ) 34 + ]; 35 + }; 36 + 37 + second = lib.evalModules { 38 + modules = [ 39 + first.config.baz.aspects.a._.b._.c.modules.nixos 40 + { options.x = lib.mkOption { type = lib.types.listOf lib.types.str; }; } 41 + ]; 42 + }; 43 + 44 + expr = second.config.x; 45 + expected = [ 46 + "foo" 47 + "bar" 48 + "baz" 49 + ]; 50 + in 51 + { 52 + inherit expected expr; 53 + }; 54 + 55 + }
+91 -66
nix/types.nix
··· 49 49 (x: lib.length x > 0) 50 50 ]; 51 51 52 - aspectSubmodule = lib.types.submodule ( 53 - { 54 - name, 55 - aspect, 56 - config, 57 - ... 58 - }: 59 - { 60 - freeformType = lib.types.attrsOf lib.types.deferredModule; 61 - config._module.args.aspect = config; 62 - imports = [ (lib.mkAliasOptionModule [ "_" ] [ "provides" ]) ]; 63 - options.name = lib.mkOption { 64 - description = "Aspect name"; 65 - default = name; 66 - type = lib.types.str; 67 - }; 68 - options.description = lib.mkOption { 69 - description = "Aspect description"; 70 - default = "Aspect ${name}"; 71 - type = lib.types.str; 72 - }; 73 - options.includes = lib.mkOption { 74 - description = "Providers to ask aspects from"; 75 - type = lib.types.listOf providerType; 76 - default = [ ]; 77 - }; 78 - options.provides = lib.mkOption { 79 - description = "Providers of aspect for other aspects"; 80 - default = { }; 81 - type = aspectsType; 82 - }; 83 - options.__functor = lib.mkOption { 84 - internal = true; 85 - visible = false; 86 - description = "Functor to default provider"; 87 - type = lib.types.functionTo providerType; 88 - default = 89 - aspect: 90 - { class, aspect-chain }: 91 - # silence nixf-diagnose :/ 92 - if true || (class aspect-chain) then aspect else aspect; 93 - }; 94 - options.modules = lib.mkOption { 95 - internal = true; 96 - visible = false; 97 - readOnly = true; 98 - description = "resolved modules from this aspect"; 99 - type = lib.types.attrsOf lib.types.deferredModule; 100 - default = lib.mapAttrs (class: _: aspect.resolve { inherit class; }) aspect; 101 - }; 102 - options.resolve = lib.mkOption { 103 - internal = true; 104 - visible = false; 105 - readOnly = true; 106 - description = "function to resolve a module from this aspect"; 107 - type = lib.types.functionTo lib.types.deferredModule; 108 - default = 109 - { 110 - class, 111 - aspect-chain ? [ ], 112 - }: 113 - resolve class aspect-chain (aspect { 114 - inherit class aspect-chain; 115 - }); 116 - }; 117 - } 52 + aspectSubmodule = aspectSubmoduleMerge ( 53 + lib.types.submodule ( 54 + { 55 + name, 56 + aspect, 57 + config, 58 + ... 59 + }: 60 + { 61 + freeformType = lib.types.attrsOf lib.types.deferredModule; 62 + config._module.args.aspect = config; 63 + imports = [ (lib.mkAliasOptionModule [ "_" ] [ "provides" ]) ]; 64 + options.name = lib.mkOption { 65 + description = "Aspect name"; 66 + default = name; 67 + type = lib.types.str; 68 + }; 69 + options.description = lib.mkOption { 70 + description = "Aspect description"; 71 + default = "Aspect ${name}"; 72 + type = lib.types.str; 73 + }; 74 + options.includes = lib.mkOption { 75 + description = "Providers to ask aspects from"; 76 + type = lib.types.listOf providerType; 77 + default = [ ]; 78 + }; 79 + options.provides = lib.mkOption { 80 + description = "Providers of aspect for other aspects"; 81 + default = { }; 82 + type = aspectsType; 83 + }; 84 + options.__functor = lib.mkOption { 85 + internal = true; 86 + visible = false; 87 + description = "Functor to default provider"; 88 + type = lib.types.functionTo providerType; 89 + default = 90 + aspect: 91 + { class, aspect-chain }: 92 + # silence nixf-diagnose :/ 93 + if true || (class aspect-chain) then aspect else aspect; 94 + }; 95 + options.modules = lib.mkOption { 96 + internal = true; 97 + visible = false; 98 + readOnly = true; 99 + description = "resolved modules from this aspect"; 100 + type = lib.types.attrsOf lib.types.deferredModule; 101 + default = lib.mapAttrs (class: _: aspect.resolve { inherit class; }) aspect; 102 + }; 103 + options.resolve = lib.mkOption { 104 + internal = true; 105 + visible = false; 106 + readOnly = true; 107 + description = "function to resolve a module from this aspect"; 108 + type = lib.types.functionTo lib.types.deferredModule; 109 + default = 110 + { 111 + class, 112 + aspect-chain ? [ ], 113 + }: 114 + resolve class aspect-chain (aspect { 115 + inherit class aspect-chain; 116 + }); 117 + }; 118 + } 119 + ) 118 120 ); 119 121 122 + aspectSubmoduleMerge = 123 + tpe: 124 + let 125 + mrg = tpe.merge; 126 + clearDef = 127 + { file, value }: 128 + { 129 + inherit file; 130 + value = 131 + if builtins.isAttrs value && value ? modules then 132 + builtins.removeAttrs value [ 133 + "resolve" 134 + "modules" 135 + "provides" 136 + ] 137 + else 138 + value; 139 + }; 140 + in 141 + tpe 142 + // { 143 + merge = loc: defs: (mrg loc (lib.map clearDef defs)); 144 + }; 120 145 in 121 146 { 122 147 inherit