cargo_edit/
fetch.rs

1use super::Dependency;
2use super::RegistrySource;
3use super::VersionExt;
4
5/// Simplified represetation of `package.rust-version`
6#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
7pub struct RustVersion {
8    #[allow(missing_docs)]
9    pub major: u64,
10    #[allow(missing_docs)]
11    pub minor: u64,
12    #[allow(missing_docs)]
13    pub patch: u64,
14}
15
16impl RustVersion {
17    /// Minimum-possible `package.rust-version`
18    pub const MIN: Self = RustVersion {
19        major: 1,
20        minor: 0,
21        patch: 0,
22    };
23    /// Maximum-possible `package.rust-version`
24    pub const MAX: Self = RustVersion {
25        major: u64::MAX,
26        minor: u64::MAX,
27        patch: u64::MAX,
28    };
29}
30
31impl std::str::FromStr for RustVersion {
32    type Err = anyhow::Error;
33
34    fn from_str(text: &str) -> Result<Self, Self::Err> {
35        let version_req = text.parse::<semver::VersionReq>()?;
36        anyhow::ensure!(
37            version_req.comparators.len() == 1,
38            "rust-version must be a value like `1.32`"
39        );
40        let comp = &version_req.comparators[0];
41        anyhow::ensure!(
42            comp.op == semver::Op::Caret,
43            "rust-version must be a value like `1.32`"
44        );
45        anyhow::ensure!(
46            comp.pre == semver::Prerelease::EMPTY,
47            "rust-version must be a value like `1.32`"
48        );
49        Ok(Self {
50            major: comp.major,
51            minor: comp.minor.unwrap_or(0),
52            patch: comp.patch.unwrap_or(0),
53        })
54    }
55}
56
57impl From<&'_ semver::VersionReq> for RustVersion {
58    fn from(version_req: &semver::VersionReq) -> Self {
59        // HACK: `rust-version` is a subset of the `VersionReq` syntax that only ever
60        // has one comparator with a required minor and optional patch, and uses no
61        // other features. If in the future this syntax is expanded, this code will need
62        // to be updated.
63        assert!(version_req.comparators.len() == 1);
64        let comp = &version_req.comparators[0];
65        assert_eq!(comp.op, semver::Op::Caret);
66        assert_eq!(comp.pre, semver::Prerelease::EMPTY);
67        Self {
68            major: comp.major,
69            minor: comp.minor.unwrap_or(0),
70            patch: comp.patch.unwrap_or(0),
71        }
72    }
73}
74
75impl From<&'_ semver::Version> for RustVersion {
76    fn from(version: &semver::Version) -> Self {
77        Self {
78            major: version.major,
79            minor: version.minor,
80            patch: version.patch,
81        }
82    }
83}
84
85// Checks whether a version object is a stable release
86fn version_is_stable(version: &semver::Version) -> bool {
87    !version.is_prerelease()
88}
89
90/// Read latest version from Versions structure
91pub fn find_latest_version(
92    versions: &[tame_index::IndexVersion],
93    flag_allow_prerelease: bool,
94    rust_version: Option<RustVersion>,
95) -> Option<Dependency> {
96    let (latest, _) = versions
97        .iter()
98        .filter_map(|k| Some((k, k.version.parse::<semver::Version>().ok()?)))
99        .filter(|(_, v)| flag_allow_prerelease || version_is_stable(v))
100        .filter(|(k, _)| !k.yanked)
101        .filter(|(k, _)| {
102            rust_version
103                .and_then(|rust_version| {
104                    k.rust_version
105                        .as_ref()
106                        .and_then(|k_rust_version| k_rust_version.parse::<RustVersion>().ok())
107                        .map(|k_rust_version| k_rust_version <= rust_version)
108                })
109                .unwrap_or(true)
110        })
111        .max_by_key(|(_, v)| v.clone())?;
112
113    let name = &latest.name;
114    let version = latest.version.to_string();
115    Some(Dependency::new(name).set_source(RegistrySource::new(version)))
116}
117
118pub fn find_compatible_version(
119    versions: &[tame_index::IndexVersion],
120    version_req: &semver::VersionReq,
121    rust_version: Option<RustVersion>,
122) -> Option<Dependency> {
123    let (latest, _) = versions
124        .iter()
125        .filter_map(|k| Some((k, k.version.parse::<semver::Version>().ok()?)))
126        .filter(|(_, v)| version_req.matches(v))
127        .filter(|(k, _)| !k.yanked)
128        .filter(|(k, _)| {
129            rust_version
130                .and_then(|rust_version| {
131                    k.rust_version
132                        .as_ref()
133                        .and_then(|k_rust_version| k_rust_version.parse::<RustVersion>().ok())
134                        .map(|k_rust_version| k_rust_version <= rust_version)
135                })
136                .unwrap_or(true)
137        })
138        .max_by_key(|(_, v)| v.clone())?;
139
140    let name = &latest.name;
141    let version = latest.version.to_string();
142    Some(Dependency::new(name).set_source(RegistrySource::new(version)))
143}