1use super::Dependency;
2use super::RegistrySource;
3use super::VersionExt;
4
5#[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 pub const MIN: Self = RustVersion {
19 major: 1,
20 minor: 0,
21 patch: 0,
22 };
23 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 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
85fn version_is_stable(version: &semver::Version) -> bool {
87 !version.is_prerelease()
88}
89
90pub 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}