{ lib }: let inherit (lib) concatLists concatStringsSep foldl' groupBy mapAttrsToList unique ; declarations = raw: concatLists ( mapAttrsToList ( _evalHost: hostDeclarations: concatLists ( mapAttrsToList ( hostName: entries: mapAttrsToList (serviceName: uid: { path = "${hostName}.${serviceName}"; inherit serviceName uid; }) entries ) hostDeclarations ) ) raw ); duplicateServices = entries: let byService = groupBy (entry: entry.serviceName) entries; in foldl' ( acc: serviceName: let serviceEntries = byService.${serviceName}; uids = unique (map (entry: entry.uid) serviceEntries); in if builtins.length uids > 1 then acc ++ [ { inherit serviceName uids; paths = map (entry: entry.path) serviceEntries; } ] else acc ) [ ] (builtins.attrNames byService); duplicateUids = entries: let byUid = groupBy (entry: toString entry.uid) entries; in foldl' ( acc: uid: let uidEntries = byUid.${uid}; serviceNames = unique (map (entry: entry.serviceName) uidEntries); in if builtins.length serviceNames > 1 then acc ++ [ { uid = lib.toInt uid; inherit serviceNames; paths = map (entry: entry.path) uidEntries; } ] else acc ) [ ] (builtins.attrNames byUid); renderServiceConflict = conflict: "${conflict.serviceName} is assigned UIDs ${concatStringsSep ", " (map toString conflict.uids)} by ${concatStringsSep ", " conflict.paths}"; renderUidConflict = conflict: "${toString conflict.uid} is assigned to ${concatStringsSep ", " conflict.serviceNames} by ${concatStringsSep ", " conflict.paths}"; in { # Next stable UID: 20003 replicate = true; type = lib.types.ints.positive; validate = raw: let entries = declarations raw; serviceConflicts = duplicateServices entries; uidConflicts = duplicateUids entries; in [ { assertion = serviceConflicts == [ ]; message = '' Conflicting stable UID allocation(s): ${concatStringsSep "\n" (map renderServiceConflict serviceConflicts)} ''; } { assertion = uidConflicts == [ ]; message = '' Duplicate stable UID allocation(s): ${concatStringsSep "\n" (map renderUidConflict uidConflicts)} ''; } ]; }