align / code /bugfind /bench /semverlite.py
viai957's picture
Add files using upload-large-folder tool
4951e76 verified
Raw History Blame Contribute Delete
2.29 kB
"""Minimal Cargo-style semver parsing/matching (enough for RustSec version ranges)."""
import re
_V = re.compile(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$")
def parse(v):
m = _V.match(v.strip())
if not m:
raise ValueError(v)
a, b, c, pre = m.groups()
return (int(a), int(b or 0), int(c or 0)), pre
def key(v):
t, pre = parse(v)
# prerelease sorts before release
return (t, 0 if pre else 1, pre or "")
def is_pre(v):
return parse(v)[1] is not None
def _parts(s):
m = _V.match(s.strip())
a, b, c, pre = m.groups()
return int(a), (None if b is None else int(b)), (None if c is None else int(c)), pre
def _cmp_one(v, op, rhs):
kv = key(v)
kr = key(rhs)
if op == "=":
a, b, c, _ = _parts(rhs)
t = kv[0]
if b is None:
return t[0] == a
if c is None:
return t[0] == a and t[1] == b
return kv == kr
if op == "<":
return kv < kr
if op == "<=":
a, b, c, _ = _parts(rhs)
if c is None: # "<= 1.2" means < 1.3 style; be permissive
hi = (a, (b if b is not None else 0), 10**9)
return kv[0] <= hi
return kv <= kr
if op == ">":
return kv > kr
if op == ">=":
return kv >= kr
if op == "^" or op == "":
a, b, c, _ = _parts(rhs)
lo = kr
if a > 0 or b is None:
hi = (a + 1, 0, 0)
elif b > 0 or c is None:
hi = (0, b + 1, 0)
else:
hi = (0, 0, c + 1)
return kv >= lo and kv[0] < hi
if op == "~":
a, b, c, _ = _parts(rhs)
hi = (a + 1, 0, 0) if b is None else (a, b + 1, 0)
return kv >= kr and kv[0] < hi
raise ValueError(op)
_C = re.compile(r"^\s*(<=|>=|=|<|>|\^|~)?\s*(\S+)\s*$")
def matches_req(v, req):
"""req: comma-separated comparator list; all must hold."""
for part in req.split(","):
m = _C.match(part)
if not m:
raise ValueError(req)
op, rhs = m.groups()
if rhs.endswith(".*"):
rhs, op = rhs[:-2], "="
if not _cmp_one(v, op or "", rhs):
return False
return True
def matches_any(v, reqs):
return any(matches_req(v, r) for r in reqs)