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
at main 104 lines 3.1 kB view raw
1{ den, lib, ... }: 2let 3 description = '' 4 An aspect that imports all modules defined for `from` class 5 into a target `into` submodule. 6 7 This can be used to create custom Nix classes that help 8 people separating concerns on huge module hierarchies. 9 10 For example, using a new `user` class that forwards all its 11 settings into `users.users.<userName>` allows: 12 13 den.aspects.alice.nixos.users.users.alice.isNormalUser = true; 14 15 to become: 16 17 den.aspects.alice.user.isNormalUser = true; 18 19 20 This is exactly how `homeManager` class support is implemented in Den. 21 See home-manager/hm-integration.nix. 22 23 Den also provides the mentioned `user` class (`den._.os-user`) for setting 24 NixOS/Darwin options under `users.users.<userName>` at os-level. 25 26 Any other user-environments like `nix-maid` or `hjem` or user-custom classes 27 are easily implemented using `den._.forward`. 28 29 Note: `den._.forward` is a high-level aspect, its result 30 is another aspect that needs to be included for the new class to exist. 31 32 See templates/ci/modules/forward.nix for usage example. 33 See also: https://github.com/vic/den/issues/160, https://github.com/vic/flake-aspects/pull/31 34 ''; 35 36 forwardEach = fwd: { 37 includes = map (item: forwardOne (fwd // { each = [ item ]; })) fwd.each; 38 }; 39 40 forwardOne = 41 { 42 guard ? null, 43 adaptArgs ? null, 44 adapterModule ? null, 45 ... 46 }@fwd: 47 let 48 clean = builtins.removeAttrs fwd [ 49 "guard" 50 "adaptArgs" 51 "adapterModule" 52 ]; 53 fromClass = fwd.fromClass (lib.head fwd.each); 54 intoClass = fwd.intoClass (lib.head fwd.each); 55 intoPath = fwd.intoPath (lib.head fwd.each); 56 freeformMod = { 57 config._module.freeformType = lib.types.lazyAttrsOf lib.types.unspecified; 58 }; 59 adapterKey = lib.concatStringsSep "_" ( 60 [ 61 fromClass 62 intoClass 63 ] 64 ++ intoPath 65 ); 66 adapter = { 67 includes = [ 68 (den.lib.aspects.forward ( 69 clean 70 // { 71 intoPath = _: [ 72 "den" 73 "fwd" 74 adapterKey 75 ]; 76 } 77 )) 78 ]; 79 ${intoClass} = args: { 80 options.den.fwd.${adapterKey} = lib.mkOption { 81 default = { }; 82 type = lib.types.submoduleWith { 83 specialArgs = if adaptArgs == null then args else adaptArgs args; 84 modules = if adapterModule == null then [ freeformMod ] else [ adapterModule ]; 85 }; 86 }; 87 config = lib.optionalAttrs (guard == null || guard args) ( 88 lib.setAttrByPath intoPath args.config.den.fwd.${adapterKey} 89 ); 90 }; 91 }; 92 93 needsAdapter = guard != null || adaptArgs != null || adapterModule != null; 94 forwarded = den.lib.aspects.forward clean; 95 in 96 if needsAdapter then adapter else forwarded; 97 98in 99{ 100 den.provides.forward = { 101 inherit description; 102 __functor = _self: forwardEach; 103 }; 104}