{ config, lib, ... }: with lib; let netOpts = { name, ... }: { options = { vlan = mkOption { description = "VLAN tag number"; type = types.int; }; subnet4 = mkOption { description = "v.w.x.y/z"; type = with types; nullOr str; default = null; }; subnets6 = mkOption { description = "IPv6 subnets w/o prefixlen (always 64)"; type = with types; attrsOf str; default = {}; }; }; }; hostOpts = { name, ... }: { options = { role = mkOption { type = types.enum [ "ap" "switch" "server" "gateway" "client" ]; default = "client"; }; model = mkOption { type = types.str; default = "pc"; }; password = mkOption { type = with types; nullOr str; default = null; }; location = mkOption { type = with types; nullOr str; default = null; }; }; }; in { options.warnings = mkOption { type = types.listOf types.str; default = []; internal = true; }; options.site = { net = mkOption { default = {}; type = with types; attrsOf (submodule netOpts); }; hosts = mkOption { default = {}; type = with types; attrsOf (submodule hostOpts); }; }; config.warnings = let findCollisions = getter: xs: (builtins.foldl' ({ known, dup }: k: let ks = builtins.toString k; in if known ? ${ks} then { inherit known; dup = dup ++ [ks]; } else { known = known // { ${ks} = true; }; inherit dup; } ) { known = {}; dup = []; } ( concatMap getter (builtins.attrValues xs) )).dup; reportCollisions = name: getter: xs: map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs); in (reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++ (reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++ (reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net); }