{ lib, self }: let inherit (lib) all concatLists concatMapStringsSep filter filterAttrs foldl' hasAttr hasSuffix mapAttrs mapAttrs' mapAttrsToList mkOption nameValuePair removeSuffix types unique ; checkType = types.submodule { options = { assertion = mkOption { type = types.functionTo types.bool; description = '' Predicate run against this registry's flattened collected entries. ''; }; message = mkOption { type = types.str; description = '' Error message shown when this registry validation check fails. ''; }; }; }; predicates = { uniqueValue = value: entries: builtins.length (unique (map value entries)) == builtins.length entries; }; registryLib = lib // { registry = (lib.registry or { }) // { inherit predicates; }; }; registryDefinitionModule = { options = { type = mkOption { type = types.raw; description = '' NixOS option type for one registry entry. ''; }; replicate = mkOption { type = types.bool; default = false; description = '' Whether collected entries are merged and published under every host key. ''; }; checks = mkOption { type = types.listOf checkType; default = [ ]; description = '' Validation checks for this registry's flattened collected entries. ''; }; }; }; registryName = fileName: removeSuffix ".nix" fileName; registryOption = registry: mkOption { type = types.attrsOf (types.attrsOf registry.type); default = { }; description = '' Lazily collected fleet-visible registry entries, nested by host and entry name. ''; }; mkRegistry = path: let evaluated = lib.evalModules { specialArgs.lib = registryLib; modules = [ registryDefinitionModule path ]; }; in { inherit (evaluated.config) type replicate checks; }; definitions = mapAttrs' ( name: type: let path = self + "/registry/${name}"; in nameValuePair (registryName name) (mkRegistry path) ) (filterAttrs (name: type: type == "regular" && hasSuffix ".nix" name) (builtins.readDir (self + "/registry"))); flattenRegistryEntries = registryName: data: concatLists ( mapAttrsToList ( host: entries: mapAttrsToList (entry: value: { path = "registry.${registryName}.${host}.${entry}"; inherit host entry value; }) entries ) data ); rawDeclarations = registryName: hosts: mapAttrs (_hostName: host: host.config.registry.${registryName} or { }) hosts; replicatedEntries = registryName: raw: let entries = concatLists ( concatLists ( mapAttrsToList ( _evalHost: hostDeclarations: mapAttrsToList ( hostName: declarations: mapAttrsToList (entryName: value: { path = "registry.${registryName}.${hostName}.${entryName}"; inherit entryName value; }) declarations ) hostDeclarations ) raw ) ); in foldl' ( acc: entry: if hasAttr entry.entryName acc then if acc.${entry.entryName}.value == entry.value then acc else throw "conflicting replicated registry entry ${entry.path} does not match ${acc.${entry.entryName}.path}" else acc // { ${entry.entryName} = entry; } ) { } entries; replicatedData = registryName: hosts: raw: let entries = mapAttrs (_entryName: entry: entry.value) (replicatedEntries registryName raw); in mapAttrs (_hostName: _host: entries) hosts; validationAssertions = data: concatLists ( mapAttrsToList ( registryName: registry: let entries = flattenRegistryEntries registryName data.${registryName}; in map (check: { assertion = check.assertion entries; inherit (check) message; }) registry.checks ) definitions ); assertValid = data: let assertions = validationAssertions data; failures = filter (assertion: !assertion.assertion) assertions; in assert lib.assertMsg (all (assertion: assertion.assertion) assertions) ( concatMapStringsSep "\n" (assertion: assertion.message) failures ); data; join = registry: host: entry: filterAttrs (_name: value: value != null) ( mapAttrs (registryName: _registry: (registry.${registryName}.${host} or { }).${entry} or null) definitions ); collectData = { hosts }: let raw = mapAttrs (registryName: _registry: rawDeclarations registryName hosts) definitions; data = mapAttrs ( registryName: registry: if registry.replicate or false then replicatedData registryName hosts raw.${registryName} else mapAttrs (hostName: host: host.config.registry.${registryName}.${hostName} or { }) hosts ) definitions; in assertValid data; withOperations = registry: registry // { join = host: entry: join registry host entry; }; in { inherit definitions predicates; module = { options.registry = mapAttrs (_name: registry: registryOption registry) definitions; }; collect = args: withOperations (collectData args); }