diff --git a/flake.nix b/flake.nix index 2faebf1..537b8fb 100644 --- a/flake.nix +++ b/flake.nix @@ -211,6 +211,7 @@ mkPkgs mkInputs mkNixosConfig + vaculib ; }; diff --git a/vaculib/ip.nix b/vaculib/ip.nix index 878cbca..1900f5b 100644 --- a/vaculib/ip.nix +++ b/vaculib/ip.nix @@ -5,13 +5,14 @@ ... }: let + toInt = lib.toIntBase10; elemAt = list: idx: let positiveIdx = if idx < 0 then (lib.length list) + idx else idx; in - lib.getElem list positiveIdx; + lib.elemAt list positiveIdx; isDigits = str: (builtins.match ''[0-9]+'' str) != null; @@ -44,7 +45,7 @@ let ipToString = obj: (ipToStringCore obj) - ++ lib.optionalString obj.hasPrefix + + lib.optionalString obj.hasPrefix "/${builtins.toString obj.prefixSize}"; isIP = obj: @@ -58,11 +59,12 @@ let inherit segments; __toString = this.ipToString; hasPrefix = false; + prefixSize = null; toSubnet = mkIP (args // { prefixSize = this.bitSize; }); } // lib.optionalAttrs (prefixSize != null) { hasPrefix = true; - toSubnet = mkIP args; inherit prefixSize; + toSubnet = mkIP args; subnetMask = mkIP { segments = lib.genList (idx: this.maskBitsToSegment (prefixSize - segmentBitSize*idx)) segmentCount; }; @@ -73,7 +75,7 @@ let subnetSplit = lib.splitString "/" str; addrStr = lib.head subnetSplit; subnetStr = if (lib.length subnetSplit) == 2 then elemAt subnetSplit 1 else null; - prefixSize = if subnetStr == null then null else lib.toInt subnetStr; + prefixSize = if subnetStr == null then null else toInt subnetStr; innerParse = innerParseStrCore addrStr; valid = (subnetStr != null -> (isDigits subnetStr) && prefixSize >= 0 && prefixSize <= this.bitSize) && innerParse.valid; in @@ -85,15 +87,16 @@ let (this.parseStrCore str).valid; parse = str: - builtins.addErrorContext ''While parsing ${lib.escapeNixString str} as ${this.versionString}'' + builtins.addErrorContext ''While parsing ${lib.strings.escapeNixString str} as ${this.versionString}'' (let innerParse = this.parseStrCore str; in - lib.throwIf (!innerParse.valid) "Invalid ${this.versionString} string" + lib.throwIf (!innerParse.valid) "Invalid ${this.versionString} string: ${lib.strings.escapeNixString str}" mkIP innerParse.mkArgs ); + zero = this.mkIP { segments = lib.genList (_: 0) this.segmentCount; }; publicMethods = { - inherit (this) mkIP isIP isValidStr parse; + inherit (this) mkIP isIP isValidStr parse zero; }; }; in this; @@ -106,18 +109,18 @@ let segmentCount = 4; segmentBitSize = 8; versionInt = 4; - mkIP = args: this.mkIPCore // { zone = null; hasZone = false; }; + mkIP = args: (this.mkIPCore args) // { zone = null; hasZone = false; }; ipToStringCore = obj: lib.pipe obj.segments [ (map builtins.toString) - (lib.concatStringSep ".") + (lib.concatStringsSep ".") ]; innerParseStrCore = addrStr: let segmentStrs = lib.splitString "." addrStr; - segments = map lib.toInt segmentStrs; + segments = map toInt segmentStrs; valid = (builtins.length segmentStrs) == 4 && (lib.all isDigits segmentStrs) && (lib.all (s: s >= 0 && s <= this.segmentValueMax) segments); in { inherit valid; } // lib.optionalAttrs valid { @@ -134,15 +137,23 @@ let inherit zone; hasZone = zone != null; }; + ipToStringCore = + obj: + (lib.pipe obj.segments [ + (map nix-colors-lib.decToHex) + (lib.concatStringSep ":") + ]) + + lib.optionalString obj.hasZone "%${obj.zone}" + ; parseSide = str: let segmentStrs = lib.splitString ":" str; segmentStrs' = if segmentStrs == [ "" ] then [] else segmentStrs; valid = lib.all isHexDigits segmentStrs; - segments = map nix-colors-lib.hexToDec segmentStrs'; + segments = map nix-colors-lib.conversions.hexToDec segmentStrs'; in - { inherit valid; } // lib.optionalAttrs valid { inherit segments; }; + ({ inherit valid; } // lib.optionalAttrs valid { inherit segments; }); parsePlain = { str, bitsParsedSeparately ? 0 }: let @@ -150,9 +161,9 @@ let sides = lib.splitString "::" str; compressed = lib.length sides == 2; leftResult = parseSide (elemAt sides 0); - leftSegments = leftResult.segments; + leftSegments = leftResult.segments or []; rightResult = if compressed then parseSide (elemAt sides 1) else { valid = true; segments = []; }; - rightSegments = rightResult.segments; + rightSegments = rightResult.segments or []; segmentCount = (lib.length leftSegments) + (lib.length rightSegments); middleSegments = lib.genList (_: 0) (segmentFullCount - segmentCount); segments = leftSegments ++ middleSegments ++ rightSegments; @@ -165,11 +176,11 @@ let && lib.all (s: s >= 0 && s <= this.segmentBitSize) segments ; in - { inherit valid; } // lib.optionalAttrs valid { inherit segments; }; + { inherit valid; } // lib.optionalAttrs valid { segments = segments; }; innerParseStrCore = addrStr: let - ip4Regex = ''([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)''; + ip4Regex = ''[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+''; ip4ToIP6Fragment = ip4: [ @@ -180,13 +191,15 @@ let (lib.removePrefix "[") (lib.removeSuffix "]") ]; - matches = builtins.match ''([0-9a-fA-F:]+)(${ip4Regex})?(%[a-zA-Z0-9_-]+)?'' withoutBrackets; + matches = builtins.match ''([0-9a-fA-F:]+)(:${ip4Regex})?(%[a-zA-Z0-9_-]+)?'' withoutBrackets; - mainMatch = elemAt matches 0; - ip4Match = elemAt matches 1; + mainMatchBlegh = elemAt matches 0; + ip4MatchBlegh = elemAt matches 1; zoneMatch = elemAt matches 2; - hasIP4 = ip4Match != null; + hasIP4 = ip4MatchBlegh != null; + ip4Match = lib.removePrefix ":" ip4MatchBlegh; + mainMatch = if hasIP4 then mainMatchBlegh + ":" else mainMatchBlegh; ip4Parse = v4Data.innerParseStrCore ip4Match; ip4Segments = ip4Parse.mkArgs.segments; @@ -200,22 +213,45 @@ let zone = if hasZone then lib.removePrefix "%" zoneMatch else null; valid = - (lib.hasPrefix "[" addrStr) <-> (lib.hasSuffix "]" addrStr) - && matches != null - && (lib.hasPrefix ":" mainMatch) -> (lib.hasPrefix "::" mainMatch) - && (lib.hasSuffix ":" mainMatch) -> ((lib.hasSuffix "::" mainMatch) || hasIP4) - && hasIP4 -> (lib.hasSuffix ":" mainMatch) - && hasIP4 -> ip4Parse.valid + (lib.hasPrefix "[" addrStr) == (lib.hasSuffix "]" addrStr) + && (matches != null) + && ((lib.hasPrefix ":" mainMatch) -> (lib.hasPrefix "::" mainMatch)) + && ((lib.hasSuffix ":" mainMatch) -> ((lib.hasSuffix "::" mainMatch) || hasIP4)) + && (hasIP4 -> (lib.hasSuffix ":" mainMatch)) + && (hasIP4 -> ip4Parse.valid) && segmentsResult.valid ; + + # debugData = { inherit mainMatch hasIP4 ip4Match; segmentValid = segmentsResult.valid; }; in assert matches != null -> (builtins.length matches) == 3; + # builtins.trace (builtins.deepSeq debugData debugData) { inherit valid; } // lib.optionalAttrs valid { mkArgs = { inherit segments zone; }; }; }); + parseIPAny = + str: + let + v4Result = v4Data.parseStrCore str; + v6Result = v6Data.parseStrCore str; + in + lib.throwIf (v4Result.valid && v6Result.valid) "${lib.strings.escapeNixString str} is valid as both IPv4 and IPv6; This shouldn't happen" + lib.throwIfNot (v4Result.valid || v6Result.valid) "${lib.strings.escapeNixString str} is not a valid IPv4 or IPv6 address" + (if v4Result.valid then v4Data.mkIP v4Result.mkArgs else v6Data.mkIP v6Result.mkArgs); + equalIPs = + a: b: + assert isIPAny a; + assert isIPAny b; + lib.all (attr: a.${attr} == b.${attr}) [ + "ipVersion" + "segments" + "hasPrefix" + "prefixSize" + "zone" + ]; methods = { - inherit isIPAny; + inherit isIPAny parseIPAny v4Data v6Data equalIPs; v4 = v4Data.publicMethods; v6 = v6Data.publicMethods; } // lib.pipe [ v4Data v6Data ] [ @@ -224,82 +260,9 @@ let "isIP${toString data.versionInt}" = data.isIP; "isValidIP${toString data.versionInt}Str" = data.isValidStr; "parseIP${toString data.versionInt}" = data.parse; + "zeroIP${toString data.versionInt}" = data.zero; })) lib.mergeAttrsList ]; in { ip = methods; } -# { ip = { -# inherit isIPAny; - - # isIP4 = obj: isIP obj && obj.ipVersion == 4; - # isIP6 = obj: isIP obj && obj.ipVersion == 6; - # - # mkIP4 = { segments, prefixSize ? null }@args: - # assert vaculib.isListWhere (seg: lib.isInt seg && seg >= 0 && seg <= 255) segments; - # assert prefixSize != null -> (lib.isInt prefixSize && prefixSize >= 0 && prefixSize <= 32); - # { - # _type = "com.shelvacu.nix.ip"; - # ipVersion = 4; - # inherit segments; - # __toString = toStringIP4; - # hasPrefix = false; - # toSubnet = mkIP4 (args // { prefixSize = 32; }); - # } // lib.optionalAttrs (prefixSize != null) { - # hasPrefix = true; - # toSubnet = mkIP4 args; - # inherit prefixSize; - # subnetMask = mkIP4 { - # segments = lib.genList (idx: bitsToIP4Segment (prefixSize - 8*idx)) 4; - # }; - # }; - # - # parseIP4 = - # str: - # builtins.addErrorContext ''While parsing ${lib.escapeNixString str} as IP4'' - # (let - # m = builtins.match ''${ip4Re}${subnetRe}'' str; - # prefixMatch = elemAt m 5; - # in - # assert m != null; - # mkIP4 { - # segments = lib.pipe m [ - # (lib.sublist 0 4) - # (map lib.toInt) - # ]; - # prefixSize = if prefixMatch != null then lib.toInt prefixMatch else null; - # }); - # - # mkIP6Subnet = { segments, prefixSize, zoneIndex }: - # assert vaculib.isListWhere (seg: lib.isInt seg && seg >= 0 && seg <= 256 * 256) segments; - # assert lib.isInt prefixSize; - # assert prefixSize >= 0; - # assert prefixSize <= 128; - # assert zoneIndex == null && lib.isString zoneIndex; - # { - # _type = "com.shelvacu.nix.ip"; - # ipVersion = 6; - # inherit segments; - # inherit prefixSize; - # inherit zoneIndex; - # }; - # - # parseIP6 = - # str: - # builtins.addErrorContext ''While parsing ${lib.escapeNixString str} as IP6'' - # (let - # segment = ''[0-9a-fA-F]{0,4}''; - # scope = ''[0-9a-zA-Z]+''; - # m = builtins.match ''((${segment}:)+(${segment}))(%${scope})?${subnetRe}'' str; - # mIP4 = builtins.match ''((${segment}:)+)${ip4Re}${subnetRe}'' str; - # prefixMatch = if m != null then elemAt m -1 else elemAt mIP4 -1; - # ip4Match = elemAt mIP4 -2; - # segments = if m != null then parseIP6Plain { str = (lib.head m); } else (parseIP6Plain { str = (lib.head mIP4); aaa = 32; }) ++ ip4ToIP6Fragment (parseIP4 ip4Match); - # in - # assert m != null || mIP4 != null; - # mkIP6Subnet { - # inherit segments; - # prefixSize = if prefixMatch != null then lib.toInt prefixMatch else 128; - # zoneIndex = if m != null then elemAt m -2 else null; - # }); -# }; } diff --git a/vaculib/lists.nix b/vaculib/lists.nix index 03ce3fb..4709532 100644 --- a/vaculib/lists.nix +++ b/vaculib/lists.nix @@ -35,4 +35,16 @@ rec { a: list: mapNamesToAttrs (_: a) list; + + /** + # Type + ``` + isListWhere :: (a -> Bool) -> [a] -> Bool + isListWhere :: (a -> Bool) -> b -> Bool + ``` + */ + isListWhere = + f: + list: + lib.isList list && lib.all f list; } diff --git a/vaculib/math.nix b/vaculib/math.nix new file mode 100644 index 0000000..2897a32 --- /dev/null +++ b/vaculib/math.nix @@ -0,0 +1,17 @@ +{ + ... +}: +rec { + # https://github.com/NixOS/nixpkgs/issues/41251#issuecomment-393660714 + pow = base: exponent: + if exponent > 0 then + let + and1 = x: (x / 2) * 2 != x; + x = pow base (exponent / 2); + in + x * x * (if and1 exponent then base else 1) + else if exponent == 0 then + 1 + else + throw "undefined"; +}