blob: cde7a83a3d512c684231061126fda85527e0d46c [file] [log] [blame]
# SPDX-License-Identifier: Apache-2.0
# Copyright © 2022-2023 Intel Corporation
"""Convert Cargo versions into Meson compatible ones."""
from __future__ import annotations
import typing as T
def convert(cargo_ver: str) -> T.List[str]:
"""Convert a Cargo compatible version into a Meson compatible one.
:param cargo_ver: The version, as Cargo specifies
:return: A list of version constraints, as Meson understands them
"""
# Cleanup, just for safety
cargo_ver = cargo_ver.strip()
cargo_vers = [c.strip() for c in cargo_ver.split(',')]
out: T.List[str] = []
for ver in cargo_vers:
# This covers >= and =< as well
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#comparison-requirements
if ver.startswith(('>', '<', '=')):
out.append(ver)
elif ver.startswith('~'):
# Rust has these tilde requirements, which means that it is >= to
# the version, but less than the next version
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements
# we convert those into a pair of constraints
v = ver[1:].split('.')
out.append(f'>= {".".join(v)}')
if len(v) == 3:
out.append(f'< {v[0]}.{int(v[1]) + 1}.0')
elif len(v) == 2:
out.append(f'< {v[0]}.{int(v[1]) + 1}')
else:
out.append(f'< {int(v[0]) + 1}')
elif '*' in ver:
# Rust has astrisk requirements,, which are like 1.* == ~1
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#wildcard-requirements
v = ver.split('.')[:-1]
if v:
out.append(f'>= {".".join(v)}')
if len(v) == 2:
out.append(f'< {v[0]}.{int(v[1]) + 1}')
elif len(v) == 1:
out.append(f'< {int(v[0]) + 1}')
else:
# a Caret version is equivalent to the default strategy
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements
if ver.startswith('^'):
ver = ver[1:]
# If there is no qualifier, then it means this or the next non-zero version
# That means that if this is `1.1.0``, then we need `>= 1.1.0` && `< 2.0.0`
# Or if we have `0.1.0`, then we need `>= 0.1.0` && `< 0.2.0`
# Or if we have `0.1`, then we need `>= 0.1.0` && `< 0.2.0`
# Or if we have `0.0.0`, then we need `< 1.0.0`
# Or if we have `0.0`, then we need `< 1.0.0`
# Or if we have `0`, then we need `< 1.0.0`
# Or if we have `0.0.3`, then we need `>= 0.0.3` && `< 0.0.4`
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-cratesio
#
# this works much like the ~ versions, but in reverse. Tilde starts
# at the patch version and works up, to the major version, while
# bare numbers start at the major version and work down to the patch
# version
vers = ver.split('.')
min_: T.List[str] = []
max_: T.List[str] = []
bumped = False
for v_ in vers:
if v_ != '0' and not bumped:
min_.append(v_)
max_.append(str(int(v_) + 1))
bumped = True
else:
min_.append(v_)
if not bumped:
max_.append('0')
# If there is no minimum, don't emit one
if set(min_) != {'0'}:
out.append('>= {}'.format('.'.join(min_)))
if set(max_) != {'0'}:
out.append('< {}'.format('.'.join(max_)))
else:
out.append('< 1')
return out