{ config, lib, ... }: with lib; { options.local.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 != 0 then "${v6config.prefix}${separator}${config.suffix}" else 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 64) (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 = "${config.prefix}::"; }; })); }; }; })); }; }