{ config, lib, ... }: with lib; { options.local.sys.nets = with lib.types; mkOption { readOnly = true; type = attrsOf (submodule ({config, ...}: { options = let v4config = config.v4; v6config = config.v6; in { hosts = mkOption { default = {}; type = attrsOf (submodule { options = { v4 = mkOption { default = null; type = nullOr (submodule ({config, ...}: { options = { suffix = mkOption { type = str; }; address = mkOption { type = str; readOnly = true; }; cidr = mkOption { type = str; readOnly = true; }; single = mkOption { type = str; readOnly = true; }; }; config = { address = if v4config.bits == 0 then config.suffix else if v4config.bits == 32 then v4config.subnet else "${v4config.prefix}.${config.suffix}"; cidr = "${config.address}/${toString v4config.bits}"; single = "${config.address}/32"; }; })); }; v6 = mkOption { default = null; type = nullOr (submodule ({config, ...}: { options = { suffix = mkOption { type = str; }; address = mkOption { type = str; readOnly = true; }; cidr = mkOption { type = str; readOnly = true; }; single = mkOption { type = str; readOnly = true; }; }; config = { address = let hextets = fragment: length (splitString ":" fragment); separator = if doubleColon then "::" else ":"; doubleColon = hextets v6config.prefix + hextets config.suffix < 8; joined = if v6config.bits == 128 then v6config.prefix else if v6config.bits == 0 then config.suffix else "${v6config.prefix}${separator}${config.suffix}"; in joined; cidr = "${config.address}/${toString v6config.bits}"; single = "${config.address}/128"; }; })); }; }; }); }; v4 = mkOption { default = null; type = nullOr (submodule ({config, ...}: { options = { bits = mkOption { type = enum [0 8 16 24 32]; }; prefix = mkOption { type = str; }; subnet = mkOption { type = str; readOnly = true; }; cidr = mkOption { type = str; readOnly = true; }; }; config = { cidr = "${config.subnet}/${toString config.bits}"; subnet = if config.bits != 0 then config.prefix + strings.replicate (4 - config.bits / 8) ".0" else "0.0.0.0"; }; })); }; v6 = mkOption { default = null; type = nullOr (submodule ({config, ...}: { options = { bits = mkOption { type = addCheck (ints.between 0 128) (b: mod b 4 == 0) // { description = "IPv6 subnet bits at nibble boundary"; }; }; prefix = mkOption { type = str; }; subnet = mkOption { type = str; readOnly = true; }; cidr = mkOption { type = str; readOnly = true; }; }; config = { cidr = "${config.subnet}/${toString config.bits}"; subnet = if config.bits == 128 || length (splitString "::" config.prefix) > 1 then config.prefix else "${config.prefix}::"; }; })); }; }; })); }; }