{lib, ...}: let l = lib // builtins; # Replace a list entry at defined index with set value ireplace = idx: value: list: l.genList (i: if i == idx then value else (l.elemAt list i)) (l.length list); orBlank = x: if x != null then x else ""; operators = let mkComparison = ret: version: v: builtins.compareVersions version v == ret; mkCaretComparison = version: v: let ver = builtins.splitVersion v; major = l.toInt (l.head ver); upper = builtins.toString (l.toInt (l.head ver) + 1); in if major == 0 then mkTildeComparison version v else operators.">=" version v && operators."<" version upper; mkTildeComparison = version: v: let ver = builtins.splitVersion v; len = l.length ver; truncated = if len > 1 then l.init ver else ver; idx = (l.length truncated) - 1; minor = l.toString (l.toInt (l.elemAt truncated idx) + 1); upper = l.concatStringsSep "." (ireplace idx minor truncated); in operators.">=" version v && operators."<" version upper; in { # Prefix operators "==" = mkComparison 0; ">" = mkComparison 1; "<" = mkComparison (-1); "!=" = v: c: !operators."==" v c; ">=" = v: c: operators."==" v c || operators.">" v c; "<=" = v: c: operators."==" v c || operators."<" v c; # Semver specific operators "~" = mkTildeComparison; "^" = mkCaretComparison; }; re = { operators = "([=>="; u = "<="; }; v = { vl = orBlank (l.elemAt mIn 0); vu = orBlank (l.elemAt mIn reLengths.version); }; } else if mUpperBound != null then { ops = { t = "-"; l = l.elemAt mUpperBound 0; u = "<"; }; v = { vl = orBlank (l.elemAt mUpperBound reLengths.operators); vu = orBlank (l.elemAt mUpperBound (reLengths.operators + reLengths.version)); }; } else if mNone != null then { ops.t = "=="; v = orBlank (l.elemAt mNone 0); } else throw ''Constraint "${constraintStr}" could not be parsed'' ); satisfiesSingleInternal = version: constraint: let inherit (parseConstraint constraint) ops v; in if ops.t == "-" then (operators."${ops.l}" version v.vl && operators."${ops.u}" version v.vu) else operators."${ops.t}" version v; # remove v from version strings: ^v1.2.3 -> ^1.2.3 # remove branch suffix: ^1.2.x-dev -> ^1.2 satisfiesSingle = version: constraint: let removeStability = c: let m = l.match "^(.*)[@][[:alpha:]]+$" c; in if m != null && l.length m >= 0 then l.head m else c; removeSuffix = c: let m = l.match "^(.*)[-][[:alpha:]]+$" c; in if m != null && l.length m >= 0 then l.head m else c; wildcard = c: let m = l.match "^([[:d:]]+.*)[.][*x]$" c; in if m != null && l.length m >= 0 then "~${l.head m}.0" else c; removeV = c: let m = l.match "^(.)*v([[:d:]]+[.].*)$" c; in if m != null && l.length m > 0 then l.concatStrings m else c; isVersionLike = c: let m = l.match "^([0-9><=!-^~*]*)$" c; in m != null && l.length m > 0; cleanConstraint = removeV (wildcard (removeSuffix (removeStability (l.removePrefix "dev-" constraint)))); cleanVersion = l.removePrefix "v" (wildcard (removeSuffix version)); in (l.elem (removeStability constraint) ["" "*"]) || (version == constraint) || ((isVersionLike cleanConstraint) && (satisfiesSingleInternal cleanVersion cleanConstraint)); trim = s: l.head (l.match "^[[:space:]]*(.*[^[:space:]])[[:space:]]*$" s); splitAlternatives = v: let # handle version alternatives: ^1.2 || ^2.0 clean = l.replaceStrings ["||"] ["|"] v; in map trim (l.splitString "|" clean); splitConjunctives = v: let clean = l.replaceStrings ["," " - " " -" "- " " as "] [" " "-" "-" "-" "##"] v; cleanInlineAlias = v: let m = l.match "^(.*)[#][#](.*)$" v; in if m != null && l.length m > 0 then l.head m else v; in map (x: trim (cleanInlineAlias x)) (l.filter (x: x != "") (l.splitString " " clean)); in rec { # matching a version with semver # 1.0.2 (~1.0.1 || >=2.1 <2.4) satisfies = version: constraint: l.any (c: l.all (satisfiesSingle version) (splitConjunctives c)) (splitAlternatives constraint); # matching multiversion like the one in `provide` with semver # (1.0|2.0) (^2.0 || 3.2 - 3.6) multiSatisfies = multiversion: constraint: l.any (version: satisfies version constraint) (splitAlternatives multiversion); }