This commit is contained in:
Shelvacu
2025-08-26 23:39:44 -07:00
committed by Shelvacu on fw
parent a4dca9524e
commit 6ab9c8178d
4 changed files with 94 additions and 101 deletions

View File

@@ -211,6 +211,7 @@
mkPkgs mkPkgs
mkInputs mkInputs
mkNixosConfig mkNixosConfig
vaculib
; ;
}; };

View File

@@ -5,13 +5,14 @@
... ...
}: }:
let let
toInt = lib.toIntBase10;
elemAt = elemAt =
list: list:
idx: idx:
let let
positiveIdx = if idx < 0 then (lib.length list) + idx else idx; positiveIdx = if idx < 0 then (lib.length list) + idx else idx;
in in
lib.getElem list positiveIdx; lib.elemAt list positiveIdx;
isDigits = isDigits =
str: str:
(builtins.match ''[0-9]+'' str) != null; (builtins.match ''[0-9]+'' str) != null;
@@ -44,7 +45,7 @@ let
ipToString = ipToString =
obj: obj:
(ipToStringCore obj) (ipToStringCore obj)
++ lib.optionalString obj.hasPrefix + lib.optionalString obj.hasPrefix
"/${builtins.toString obj.prefixSize}"; "/${builtins.toString obj.prefixSize}";
isIP = isIP =
obj: obj:
@@ -58,11 +59,12 @@ let
inherit segments; inherit segments;
__toString = this.ipToString; __toString = this.ipToString;
hasPrefix = false; hasPrefix = false;
prefixSize = null;
toSubnet = mkIP (args // { prefixSize = this.bitSize; }); toSubnet = mkIP (args // { prefixSize = this.bitSize; });
} // lib.optionalAttrs (prefixSize != null) { } // lib.optionalAttrs (prefixSize != null) {
hasPrefix = true; hasPrefix = true;
toSubnet = mkIP args;
inherit prefixSize; inherit prefixSize;
toSubnet = mkIP args;
subnetMask = mkIP { subnetMask = mkIP {
segments = lib.genList (idx: this.maskBitsToSegment (prefixSize - segmentBitSize*idx)) segmentCount; segments = lib.genList (idx: this.maskBitsToSegment (prefixSize - segmentBitSize*idx)) segmentCount;
}; };
@@ -73,7 +75,7 @@ let
subnetSplit = lib.splitString "/" str; subnetSplit = lib.splitString "/" str;
addrStr = lib.head subnetSplit; addrStr = lib.head subnetSplit;
subnetStr = if (lib.length subnetSplit) == 2 then elemAt subnetSplit 1 else null; 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; innerParse = innerParseStrCore addrStr;
valid = (subnetStr != null -> (isDigits subnetStr) && prefixSize >= 0 && prefixSize <= this.bitSize) && innerParse.valid; valid = (subnetStr != null -> (isDigits subnetStr) && prefixSize >= 0 && prefixSize <= this.bitSize) && innerParse.valid;
in in
@@ -85,15 +87,16 @@ let
(this.parseStrCore str).valid; (this.parseStrCore str).valid;
parse = parse =
str: str:
builtins.addErrorContext ''While parsing ${lib.escapeNixString str} as ${this.versionString}'' builtins.addErrorContext ''While parsing ${lib.strings.escapeNixString str} as ${this.versionString}''
(let (let
innerParse = this.parseStrCore str; innerParse = this.parseStrCore str;
in in
lib.throwIf (!innerParse.valid) "Invalid ${this.versionString} string" lib.throwIf (!innerParse.valid) "Invalid ${this.versionString} string: ${lib.strings.escapeNixString str}"
mkIP innerParse.mkArgs mkIP innerParse.mkArgs
); );
zero = this.mkIP { segments = lib.genList (_: 0) this.segmentCount; };
publicMethods = { publicMethods = {
inherit (this) mkIP isIP isValidStr parse; inherit (this) mkIP isIP isValidStr parse zero;
}; };
}; };
in this; in this;
@@ -106,18 +109,18 @@ let
segmentCount = 4; segmentCount = 4;
segmentBitSize = 8; segmentBitSize = 8;
versionInt = 4; versionInt = 4;
mkIP = args: this.mkIPCore // { zone = null; hasZone = false; }; mkIP = args: (this.mkIPCore args) // { zone = null; hasZone = false; };
ipToStringCore = ipToStringCore =
obj: obj:
lib.pipe obj.segments [ lib.pipe obj.segments [
(map builtins.toString) (map builtins.toString)
(lib.concatStringSep ".") (lib.concatStringsSep ".")
]; ];
innerParseStrCore = innerParseStrCore =
addrStr: addrStr:
let let
segmentStrs = lib.splitString "." addrStr; 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); valid = (builtins.length segmentStrs) == 4 && (lib.all isDigits segmentStrs) && (lib.all (s: s >= 0 && s <= this.segmentValueMax) segments);
in in
{ inherit valid; } // lib.optionalAttrs valid { { inherit valid; } // lib.optionalAttrs valid {
@@ -134,15 +137,23 @@ let
inherit zone; inherit zone;
hasZone = zone != null; hasZone = zone != null;
}; };
ipToStringCore =
obj:
(lib.pipe obj.segments [
(map nix-colors-lib.decToHex)
(lib.concatStringSep ":")
])
+ lib.optionalString obj.hasZone "%${obj.zone}"
;
parseSide = parseSide =
str: str:
let let
segmentStrs = lib.splitString ":" str; segmentStrs = lib.splitString ":" str;
segmentStrs' = if segmentStrs == [ "" ] then [] else segmentStrs; segmentStrs' = if segmentStrs == [ "" ] then [] else segmentStrs;
valid = lib.all isHexDigits segmentStrs; valid = lib.all isHexDigits segmentStrs;
segments = map nix-colors-lib.hexToDec segmentStrs'; segments = map nix-colors-lib.conversions.hexToDec segmentStrs';
in in
{ inherit valid; } // lib.optionalAttrs valid { inherit segments; }; ({ inherit valid; } // lib.optionalAttrs valid { inherit segments; });
parsePlain = parsePlain =
{ str, bitsParsedSeparately ? 0 }: { str, bitsParsedSeparately ? 0 }:
let let
@@ -150,9 +161,9 @@ let
sides = lib.splitString "::" str; sides = lib.splitString "::" str;
compressed = lib.length sides == 2; compressed = lib.length sides == 2;
leftResult = parseSide (elemAt sides 0); leftResult = parseSide (elemAt sides 0);
leftSegments = leftResult.segments; leftSegments = leftResult.segments or [];
rightResult = if compressed then parseSide (elemAt sides 1) else { valid = true; segments = []; }; 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); segmentCount = (lib.length leftSegments) + (lib.length rightSegments);
middleSegments = lib.genList (_: 0) (segmentFullCount - segmentCount); middleSegments = lib.genList (_: 0) (segmentFullCount - segmentCount);
segments = leftSegments ++ middleSegments ++ rightSegments; segments = leftSegments ++ middleSegments ++ rightSegments;
@@ -165,11 +176,11 @@ let
&& lib.all (s: s >= 0 && s <= this.segmentBitSize) segments && lib.all (s: s >= 0 && s <= this.segmentBitSize) segments
; ;
in in
{ inherit valid; } // lib.optionalAttrs valid { inherit segments; }; { inherit valid; } // lib.optionalAttrs valid { segments = segments; };
innerParseStrCore = innerParseStrCore =
addrStr: addrStr:
let let
ip4Regex = ''([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)''; ip4Regex = ''[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'';
ip4ToIP6Fragment = ip4ToIP6Fragment =
ip4: ip4:
[ [
@@ -180,13 +191,15 @@ let
(lib.removePrefix "[") (lib.removePrefix "[")
(lib.removeSuffix "]") (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; mainMatchBlegh = elemAt matches 0;
ip4Match = elemAt matches 1; ip4MatchBlegh = elemAt matches 1;
zoneMatch = elemAt matches 2; 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; ip4Parse = v4Data.innerParseStrCore ip4Match;
ip4Segments = ip4Parse.mkArgs.segments; ip4Segments = ip4Parse.mkArgs.segments;
@@ -200,22 +213,45 @@ let
zone = if hasZone then lib.removePrefix "%" zoneMatch else null; zone = if hasZone then lib.removePrefix "%" zoneMatch else null;
valid = valid =
(lib.hasPrefix "[" addrStr) <-> (lib.hasSuffix "]" addrStr) (lib.hasPrefix "[" addrStr) == (lib.hasSuffix "]" addrStr)
&& matches != null && (matches != null)
&& (lib.hasPrefix ":" mainMatch) -> (lib.hasPrefix "::" mainMatch) && ((lib.hasPrefix ":" mainMatch) -> (lib.hasPrefix "::" mainMatch))
&& (lib.hasSuffix ":" mainMatch) -> ((lib.hasSuffix "::" mainMatch) || hasIP4) && ((lib.hasSuffix ":" mainMatch) -> ((lib.hasSuffix "::" mainMatch) || hasIP4))
&& hasIP4 -> (lib.hasSuffix ":" mainMatch) && (hasIP4 -> (lib.hasSuffix ":" mainMatch))
&& hasIP4 -> ip4Parse.valid && (hasIP4 -> ip4Parse.valid)
&& segmentsResult.valid && segmentsResult.valid
; ;
# debugData = { inherit mainMatch hasIP4 ip4Match; segmentValid = segmentsResult.valid; };
in in
assert matches != null -> (builtins.length matches) == 3; assert matches != null -> (builtins.length matches) == 3;
# builtins.trace (builtins.deepSeq debugData debugData)
{ inherit valid; } // lib.optionalAttrs valid { { inherit valid; } // lib.optionalAttrs valid {
mkArgs = { inherit segments zone; }; 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 = { methods = {
inherit isIPAny; inherit isIPAny parseIPAny v4Data v6Data equalIPs;
v4 = v4Data.publicMethods; v4 = v4Data.publicMethods;
v6 = v6Data.publicMethods; v6 = v6Data.publicMethods;
} // lib.pipe [ v4Data v6Data ] [ } // lib.pipe [ v4Data v6Data ] [
@@ -224,82 +260,9 @@ let
"isIP${toString data.versionInt}" = data.isIP; "isIP${toString data.versionInt}" = data.isIP;
"isValidIP${toString data.versionInt}Str" = data.isValidStr; "isValidIP${toString data.versionInt}Str" = data.isValidStr;
"parseIP${toString data.versionInt}" = data.parse; "parseIP${toString data.versionInt}" = data.parse;
"zeroIP${toString data.versionInt}" = data.zero;
})) }))
lib.mergeAttrsList lib.mergeAttrsList
]; ];
in in
{ ip = methods; } { 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;
# });
# }; }

View File

@@ -35,4 +35,16 @@ rec {
a: a:
list: list:
mapNamesToAttrs (_: 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;
} }

17
vaculib/math.nix Normal file
View File

@@ -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";
}