{ core, nixpkgs, secrets, vault, web, ... } @ libs: let inherit(core) context debug derivation lambda library list path set string type; inherit(secrets.secret) Secret; inherit(secrets.vault) Vault; inherit(web.html) HTML; Configuration = let debug' = debug "Configuration"; defaultCall = { environment, host, ... }: { arguments, configuration, wrap, ... } @ this: debug'.error "defaultCall" { text = '' ${type.format this} should not be a function, that depends on `config`. ''; data = this; when = list.find "config" arguments; } wrap ( this // { configuration = configuration (environment // { inherit host; }); } ); defaultWrap = { configuration, ... }: configuration; toLegacy = theVault: let fix = source: value: let result = fixDictionary source (set.expect value); in debug.debug "fix" { text = "Config of ${context.formatRelative source}"; data = value; showType = false; } debug.debug "fix" { text = "Secrets of ${context.formatRelative source}"; data = result.secrets; nice = true; when = result.secrets != {}; } result.value // ( set.ifOrEmpty (result.secrets != {}) { vault = (result.value.vault or {}) // { secrets = if result.value ? vault.secrets then Vault.update result.value.vault.secrets result.secrets else result.secrets; }; } ); fixInner = source: value: debug.debug "fixInner" { text = "Option ${context.formatRelative source}"; data = value; when = !set.isInstanceOf value; } type.matchPrimitiveOrDefault value { list = fixList source value; set = fixSet source value; } { secrets = {}; inherit value; }; fixList = source: value: debug.debug "fixList" { text = "Option ${context.formatRelative source}"; data = value; } list.fold ( { index, secrets, value }: entry: let result = fixInner (source index) entry; in { secrets = Vault.update secrets result.secrets; index = index + 1; value = value ++ [ result.value ]; } ) { secrets = {}; index = 0; value = []; } value; fixDictionary = source: value: debug.debug "fixDictionary" { text = "Option ${context.formatRelative source}"; data = value; } set.fold ( { secrets, value }: attribute: entry: let result = fixInner (source attribute) entry; in if debug.Debug.isInstanceOf result then "${result}" else { secrets = Vault.update secrets result.secrets; value = value // { ${attribute} = result.value; }; } ) { secrets = {}; value = {}; } value; fixSet = source: { ... } @ value: debug.debug "fixSet" { text = "Option ${context.formatRelative source}"; data = value; when = value._type or null == null; } ( if derivation.isInstanceOf' value || value._type or null != null || HTML.isInstanceOf value then { inherit value; secrets = {}; } else if type.getType value == null then fixDictionary source value else if debug.Debug.isInstanceOf value then abort "${value}" else if Secret.isInstanceOf value then theVault source value else debug'.panic [ "toLegacy" "fixSet" ] { text = "Got Object in ${context.formatRelative source}:"; data = value; } null ); in args: { call, configuration, source, wrap, ... } @ this: let this' = this // { arguments = set.names (lambda.arguments configuration); wrap = wrap'; }; wrap' = x: fix source (wrap x); imports = type.matchPrimitiveOrPanic configuration { lambda = [ (call args this') ]; set = [ (wrap' this') ]; }; in { #inherit source; _file = context.formatFileName source; imports = imports; }; in type "Configuration" { from = variant: { call ? defaultCall, wrap ? defaultWrap, }: { configuration, source, ... } @ config: Configuration.instanciateAs variant ( config // { inherit call toLegacy wrap; } ); }; Configuration' = variant: Configuration variant {}; LegacyConfiguration = Configuration "Legacy" { call = { ... }: { configuration, ... }: configuration; }; collect = let collectPath = { source, ... } @ this: fileName: collect ( this // { configuration = path.import fileName; source = source { inherit fileName; }; } ); in { configuration, ... } @ this: type.matchPrimitiveOrPanic configuration { lambda = [ this ]; list = list.concatMap (configuration: collect (this // { inherit configuration; }) ) configuration; null = []; # Just Ignore path = collectPath this configuration; set = [ this ]; string = collectPath this configuration; }; load = library.import ./load.nix libs; mapToArguments = list.map ( config: let config' = Configuration.expect config; arguments = set.names (lambda.arguments config'.configuration); in debug.warn "mapToArguments" { text = "Unknown Source"; data = config'; when = config'.source == null; } type.matchPrimitiveOrPanic config'.configuration { lambda = "${type.getVariant config'}: { ${string.concatWith ", " arguments} } from ${context.formatRelative config'.source}"; set = "${type.getVariant config'}: – from ${context.formatRelative config'.source}"; } ); mapToLegacy = { configurations, environment, host, modules, ... }: let theVault = Vault {}; configurations' = list.map ( { toLegacy, ... } @ cfg: toLegacy theVault { inherit host modules; environment = environment // { inherit(host) users; inherit(config) config; inherit(config.config) websites; }; } cfg ) configurations; modules' = (set.values modules) ++ [ web.module vault ] ++ configurations'; config = debug.info "mapToLegacy" { text = "modules'"; data = modules'; nice = true; } debug.debug "mapToLegacy" { text = "environment"; data = set.names environment; nice = true; when = false; } debug.debug "mapToLegacy" { text = "nixosSystem"; show = true; nice = true; when = false; } ( nixpkgs.lib.nixosSystem { baseModules = []; extraModules = []; inherit(nixpkgs) lib; modules = modules'; modulesLocation = null; pkgs = null; prefix = []; specialArgs = {}; system = null; } ); in config; sortUniqueChecked = configurations: let configurations' = list.fold ( result: config: let config' = Configuration.expect config; result' = insert result config'; in debug.panic "sortUniqueChecked" { text = "Configuration expected"; data = config; when = !(Configuration.isInstanceOf config); } debug.panic "sortUniqueChecked" { text = "Could not insert"; show = true; when = debug.Debug.isInstanceOf result'; } result' ) {} configurations; insert = { ... } @ result: { configuration, source, ... } @ config: let stringContext = string.getContext key; key = "${context.formatRelative source}"; # Should be safe, as long the keys of resulting attribute set are discarded? key' = "${type.getVariant config} ${string.discardContext key}"; value = result.${key'} or null; in debug.debug "sortUniqueChecked" { text = "Remove String-Context of ${key}"; data = stringContext; when = stringContext != {}; } ( if configuration == {} || configuration == null then result else if value == null then result // { ${key'} = config; } else # Brackets are necessary to compare functions. # See: https://cs.tvl.fyi/depot/-/blob/tvix/docs/value-pointer-equality.md if [ value ] == [ config ] then result else debug.panic "sortUniqueChecked" { text = "Confliciting configurations from the same source ${key'} o.O"; data = { prev = value; next = config; }; nice = true; } result ); in set.values configurations'; in { inherit Configuration Configuration' LegacyConfiguration; inherit collect load mapToArguments mapToLegacy sortUniqueChecked; }