{ lib }: let /** Creates a scope string for global exports At least one of serviceName or machineName must be set. The scope string has the format: "/SERVICE/INSTANCE/ROLE/MACHINE" If the parameter is not set, the corresponding part is left empty. Semantically this means "all". Examples: mkScope { serviceName = "A"; } -> "/A///" mkScope { machineName = "jon"; } -> "///jon" mkScope { serviceName = "A"; instanceName = "i1"; roleName = "peer"; machineName = "jon"; } -> "/A/i1/peer/jon" */ mkScope = { serviceName ? "", instanceName ? "", roleName ? "", machineName ? "", }: let parts = [ serviceName instanceName roleName machineName ]; checkedParts = lib.map ( part: lib.throwIf (builtins.match ".?/.?" part != null) '' clanLib.exports.mkScope: ${part} cannot contain the "/" character '' ) parts; in lib.throwIf ((serviceName == "" && machineName == "")) '' clanLib.exports.mkScope requires at least 'serviceName' or 'machineName' to be set In case your use case requires neither '' (lib.join "/" checkedParts); /** Parses a scope string into its components Returns an attribute set with the keys: - serviceName - instanceName - roleName - machineName Example: parseScope "A/i1/peer/jon" -> { serviceName = "A"; instanceName = "i1"; roleName = "peer"; machineName = "jon"; } */ parseScope = scopeStr: let parts = lib.splitString "/" scopeStr; checkedParts = lib.throwIf (lib.length parts != 4) '' clanLib.exports.parseScope: invalid scope string format, expected 4 parts separated by 3 "/" '' (parts); in { serviceName = lib.elemAt 0 checkedParts; instanceName = lib.elemAt 1 checkedParts; roleName = lib.elemAt 2 checkedParts; machineName = lib.elemAt 3 checkedParts; }; in { inherit mkScope parseScope; }