Skip to main content

mars_agents/resolve/
constraint.rs

1use semver::{Version, VersionReq};
2
3use super::VersionConstraint;
4
5/// Parse a version string into a constraint.
6///
7/// - `None` / `"latest"` → Latest (any version, newest wins)
8/// - `"v1.2.3"` → exact match
9/// - `"v2"` → `>=2.0.0, <3.0.0` (major range)
10/// - `"v2.1"` → `>=2.1.0, <2.2.0` (minor range)
11/// - `">=0.5.0"`, `"^2.0"`, `"~1.2"` → semver requirement
12/// - anything else → branch/commit ref pin
13pub fn parse_version_constraint(version: Option<&str>) -> VersionConstraint {
14    let version = match version {
15        None => return VersionConstraint::Latest,
16        Some(v) => v.trim(),
17    };
18
19    if version.is_empty() || version.eq_ignore_ascii_case("latest") {
20        return VersionConstraint::Latest;
21    }
22
23    // Try "v"-prefixed versions: v1.2.3, v2, v2.1
24    if let Some(stripped) = version.strip_prefix('v') {
25        // Try exact semver: v1.2.3
26        if let Ok(ver) = Version::parse(stripped) {
27            let req = VersionReq::parse(&format!("={ver}")).expect("valid exact req");
28            return VersionConstraint::Semver(req);
29        }
30
31        // Try major-only: v2 → >=2.0.0, <3.0.0
32        if let Ok(major) = stripped.parse::<u64>() {
33            let req = VersionReq::parse(&format!(">={major}.0.0, <{}.0.0", major + 1))
34                .expect("valid major range req");
35            return VersionConstraint::Semver(req);
36        }
37
38        // Try major.minor: v2.1 → >=2.1.0, <2.2.0
39        let parts: Vec<&str> = stripped.split('.').collect();
40        if parts.len() == 2
41            && let (Ok(major), Ok(minor)) = (parts[0].parse::<u64>(), parts[1].parse::<u64>())
42        {
43            let req = VersionReq::parse(&format!(">={major}.{minor}.0, <{major}.{}.0", minor + 1))
44                .expect("valid minor range req");
45            return VersionConstraint::Semver(req);
46        }
47    }
48
49    // Try as semver requirement directly (>=0.5.0, ^2.0, ~1.2, =1.0.0, etc.)
50    if let Ok(req) = VersionReq::parse(version) {
51        return VersionConstraint::Semver(req);
52    }
53
54    // Otherwise it's a branch or commit ref pin
55    VersionConstraint::RefPin(version.to_string())
56}