1use std::str::FromStr;
2
3use super::errors::*;
4
5pub trait VersionExt {
7 fn increment_major(&mut self);
9 fn increment_minor(&mut self);
11 fn increment_patch(&mut self);
13 fn increment_alpha(&mut self) -> CargoResult<()>;
19 fn increment_beta(&mut self) -> CargoResult<()>;
25 fn increment_rc(&mut self) -> CargoResult<()>;
31 fn metadata(&mut self, metadata: &str) -> CargoResult<()>;
33 fn is_prerelease(&self) -> bool;
35}
36
37impl VersionExt for semver::Version {
38 fn increment_major(&mut self) {
39 self.major += 1;
40 self.minor = 0;
41 self.patch = 0;
42 self.pre = semver::Prerelease::EMPTY;
43 self.build = semver::BuildMetadata::EMPTY;
44 }
45
46 fn increment_minor(&mut self) {
47 self.minor += 1;
48 self.patch = 0;
49 self.pre = semver::Prerelease::EMPTY;
50 self.build = semver::BuildMetadata::EMPTY;
51 }
52
53 fn increment_patch(&mut self) {
54 self.patch += 1;
55 self.pre = semver::Prerelease::EMPTY;
56 self.build = semver::BuildMetadata::EMPTY;
57 }
58
59 fn increment_alpha(&mut self) -> CargoResult<()> {
60 if let Some((pre_ext, pre_ext_ver)) = prerelease_id_version(self)? {
61 if pre_ext == VERSION_BETA || pre_ext == VERSION_RC {
62 Err(invalid_release_level(VERSION_ALPHA, self.clone()))
63 } else {
64 let new_ext_ver = if pre_ext == VERSION_ALPHA {
65 pre_ext_ver.unwrap_or(0) + 1
66 } else {
67 1
68 };
69 self.pre = semver::Prerelease::new(&format!("{}.{}", VERSION_ALPHA, new_ext_ver))?;
70 Ok(())
71 }
72 } else {
73 self.increment_patch();
74 self.pre = semver::Prerelease::new(&format!("{}.1", VERSION_ALPHA))?;
75 Ok(())
76 }
77 }
78
79 fn increment_beta(&mut self) -> CargoResult<()> {
80 if let Some((pre_ext, pre_ext_ver)) = prerelease_id_version(self)? {
81 if pre_ext == VERSION_RC {
82 Err(invalid_release_level(VERSION_BETA, self.clone()))
83 } else {
84 let new_ext_ver = if pre_ext == VERSION_BETA {
85 pre_ext_ver.unwrap_or(0) + 1
86 } else {
87 1
88 };
89 self.pre = semver::Prerelease::new(&format!("{}.{}", VERSION_BETA, new_ext_ver))?;
90 Ok(())
91 }
92 } else {
93 self.increment_patch();
94 self.pre = semver::Prerelease::new(&format!("{}.1", VERSION_BETA))?;
95 Ok(())
96 }
97 }
98
99 fn increment_rc(&mut self) -> CargoResult<()> {
100 if let Some((pre_ext, pre_ext_ver)) = prerelease_id_version(self)? {
101 let new_ext_ver = if pre_ext == VERSION_RC {
102 pre_ext_ver.unwrap_or(0) + 1
103 } else {
104 1
105 };
106 self.pre = semver::Prerelease::new(&format!("{}.{}", VERSION_RC, new_ext_ver))?;
107 Ok(())
108 } else {
109 self.increment_patch();
110 self.pre = semver::Prerelease::new(&format!("{}.1", VERSION_RC))?;
111 Ok(())
112 }
113 }
114
115 fn metadata(&mut self, build: &str) -> CargoResult<()> {
116 self.build = semver::BuildMetadata::new(build)?;
117 Ok(())
118 }
119
120 fn is_prerelease(&self) -> bool {
121 !self.pre.is_empty()
122 }
123}
124
125static VERSION_ALPHA: &str = "alpha";
126static VERSION_BETA: &str = "beta";
127static VERSION_RC: &str = "rc";
128
129fn prerelease_id_version(version: &semver::Version) -> CargoResult<Option<(String, Option<u64>)>> {
130 if !version.pre.is_empty() {
131 if let Some((alpha, numeric)) = version.pre.as_str().split_once('.') {
132 let alpha = alpha.to_owned();
133 let numeric = u64::from_str(numeric)
134 .map_err(|_| anyhow::format_err!("This version scheme is not supported. Use format like `pre`, `dev` or `alpha.1` for prerelease symbol"))?;
135 Ok(Some((alpha, Some(numeric))))
136 } else {
137 Ok(Some((version.pre.as_str().to_owned(), None)))
138 }
139 } else {
140 Ok(None)
141 }
142}
143
144pub fn upgrade_requirement(req: &str, version: &semver::Version) -> CargoResult<Option<String>> {
146 let req_text = req.to_string();
147 let raw_req = semver::VersionReq::parse(&req_text)
148 .expect("semver to generate valid version requirements");
149 if raw_req.comparators.is_empty() {
150 Ok(None)
152 } else {
153 let comparators: CargoResult<Vec<_>> = raw_req
154 .comparators
155 .into_iter()
156 .map(|p| set_comparator(p, version))
157 .collect();
158 let comparators = comparators?;
159 let new_req = semver::VersionReq { comparators };
160 let mut new_req_text = new_req.to_string();
161 if new_req_text.starts_with('^') && !req.starts_with('^') {
162 new_req_text.remove(0);
163 }
164 #[cfg(debug_assert)]
166 {
167 assert!(
168 new_req.matches(version),
169 "Invalid req created: {}",
170 new_req_text
171 )
172 }
173 if new_req_text == req_text {
174 Ok(None)
175 } else {
176 Ok(Some(new_req_text))
177 }
178 }
179}
180
181fn set_comparator(
182 mut pred: semver::Comparator,
183 version: &semver::Version,
184) -> CargoResult<semver::Comparator> {
185 match pred.op {
186 semver::Op::Wildcard => {
187 pred.major = version.major;
188 if pred.minor.is_some() {
189 pred.minor = Some(version.minor);
190 }
191 if pred.patch.is_some() {
192 pred.patch = Some(version.patch);
193 }
194 Ok(pred)
195 }
196 semver::Op::Exact => Ok(assign_partial_req(version, pred)),
197 semver::Op::Greater | semver::Op::GreaterEq | semver::Op::Less | semver::Op::LessEq => {
198 let user_pred = pred.to_string();
199 Err(unsupported_version_req(user_pred))
200 }
201 semver::Op::Tilde => Ok(assign_partial_req(version, pred)),
202 semver::Op::Caret => Ok(assign_partial_req(version, pred)),
203 _ => {
204 let user_pred = pred.to_string();
205 Err(unsupported_version_req(user_pred))
206 }
207 }
208}
209
210fn assign_partial_req(
211 version: &semver::Version,
212 mut pred: semver::Comparator,
213) -> semver::Comparator {
214 pred.major = version.major;
215 if pred.minor.is_some() {
216 pred.minor = Some(version.minor);
217 }
218 if pred.patch.is_some() {
219 pred.patch = Some(version.patch);
220 }
221 pred.pre = version.pre.clone();
222 pred
223}
224
225#[cfg(test)]
226mod test {
227 use super::*;
228
229 mod increment {
230 use super::*;
231
232 #[test]
233 fn alpha() {
234 let mut v = semver::Version::parse("1.0.0").unwrap();
235 v.increment_alpha().unwrap();
236 assert_eq!(v, semver::Version::parse("1.0.1-alpha.1").unwrap());
237
238 let mut v2 = semver::Version::parse("1.0.1-dev").unwrap();
239 v2.increment_alpha().unwrap();
240 assert_eq!(v2, semver::Version::parse("1.0.1-alpha.1").unwrap());
241
242 let mut v3 = semver::Version::parse("1.0.1-alpha.1").unwrap();
243 v3.increment_alpha().unwrap();
244 assert_eq!(v3, semver::Version::parse("1.0.1-alpha.2").unwrap());
245
246 let mut v4 = semver::Version::parse("1.0.1-beta.1").unwrap();
247 assert!(v4.increment_alpha().is_err());
248 }
249
250 #[test]
251 fn beta() {
252 let mut v = semver::Version::parse("1.0.0").unwrap();
253 v.increment_beta().unwrap();
254 assert_eq!(v, semver::Version::parse("1.0.1-beta.1").unwrap());
255
256 let mut v2 = semver::Version::parse("1.0.1-dev").unwrap();
257 v2.increment_beta().unwrap();
258 assert_eq!(v2, semver::Version::parse("1.0.1-beta.1").unwrap());
259
260 let mut v2 = semver::Version::parse("1.0.1-alpha.1").unwrap();
261 v2.increment_beta().unwrap();
262 assert_eq!(v2, semver::Version::parse("1.0.1-beta.1").unwrap());
263
264 let mut v3 = semver::Version::parse("1.0.1-beta.1").unwrap();
265 v3.increment_beta().unwrap();
266 assert_eq!(v3, semver::Version::parse("1.0.1-beta.2").unwrap());
267
268 let mut v4 = semver::Version::parse("1.0.1-rc.1").unwrap();
269 assert!(v4.increment_beta().is_err());
270 }
271
272 #[test]
273 fn rc() {
274 let mut v = semver::Version::parse("1.0.0").unwrap();
275 v.increment_rc().unwrap();
276 assert_eq!(v, semver::Version::parse("1.0.1-rc.1").unwrap());
277
278 let mut v2 = semver::Version::parse("1.0.1-dev").unwrap();
279 v2.increment_rc().unwrap();
280 assert_eq!(v2, semver::Version::parse("1.0.1-rc.1").unwrap());
281
282 let mut v3 = semver::Version::parse("1.0.1-rc.1").unwrap();
283 v3.increment_rc().unwrap();
284 assert_eq!(v3, semver::Version::parse("1.0.1-rc.2").unwrap());
285 }
286
287 #[test]
288 fn metadata() {
289 let mut v = semver::Version::parse("1.0.0").unwrap();
290 v.metadata("git.123456").unwrap();
291 assert_eq!(v, semver::Version::parse("1.0.0+git.123456").unwrap());
292 }
293 }
294
295 mod upgrade_requirement {
296 use super::*;
297
298 #[track_caller]
299 fn assert_req_bump<'a, O: Into<Option<&'a str>>>(version: &str, req: &str, expected: O) {
300 let version = semver::Version::parse(version).unwrap();
301 let actual = upgrade_requirement(req, &version).unwrap();
302 let expected = expected.into();
303 assert_eq!(actual.as_deref(), expected);
304 }
305
306 #[test]
307 fn wildcard_major() {
308 assert_req_bump("1.0.0", "*", None);
309 }
310
311 #[test]
312 fn wildcard_minor() {
313 assert_req_bump("1.0.0", "1.*", None);
314 assert_req_bump("1.1.0", "1.*", None);
315 assert_req_bump("2.0.0", "1.*", "2.*");
316 }
317
318 #[test]
319 fn wildcard_patch() {
320 assert_req_bump("1.0.0", "1.0.*", None);
321 assert_req_bump("1.1.0", "1.0.*", "1.1.*");
322 assert_req_bump("1.1.1", "1.0.*", "1.1.*");
323 assert_req_bump("2.0.0", "1.0.*", "2.0.*");
324 }
325
326 #[test]
327 fn caret_major() {
328 assert_req_bump("1.0.0", "1", None);
329 assert_req_bump("1.0.0", "^1", None);
330
331 assert_req_bump("1.1.0", "1", None);
332 assert_req_bump("1.1.0", "^1", None);
333
334 assert_req_bump("2.0.0", "1", "2");
335 assert_req_bump("2.0.0", "^1", "^2");
336 }
337
338 #[test]
339 fn caret_minor() {
340 assert_req_bump("1.0.0", "1.0", None);
341 assert_req_bump("1.0.0", "^1.0", None);
342
343 assert_req_bump("1.1.0", "1.0", "1.1");
344 assert_req_bump("1.1.0", "^1.0", "^1.1");
345
346 assert_req_bump("1.1.1", "1.0", "1.1");
347 assert_req_bump("1.1.1", "^1.0", "^1.1");
348
349 assert_req_bump("2.0.0", "1.0", "2.0");
350 assert_req_bump("2.0.0", "^1.0", "^2.0");
351 }
352
353 #[test]
354 fn caret_patch() {
355 assert_req_bump("1.0.0", "1.0.0", None);
356 assert_req_bump("1.0.0", "^1.0.0", None);
357
358 assert_req_bump("1.1.0", "1.0.0", "1.1.0");
359 assert_req_bump("1.1.0", "^1.0.0", "^1.1.0");
360
361 assert_req_bump("1.1.1", "1.0.0", "1.1.1");
362 assert_req_bump("1.1.1", "^1.0.0", "^1.1.1");
363
364 assert_req_bump("2.0.0", "1.0.0", "2.0.0");
365 assert_req_bump("2.0.0", "^1.0.0", "^2.0.0");
366 }
367
368 #[test]
369 fn tilde_major() {
370 assert_req_bump("1.0.0", "~1", None);
371 assert_req_bump("1.1.0", "~1", None);
372 assert_req_bump("2.0.0", "~1", "~2");
373 }
374
375 #[test]
376 fn tilde_minor() {
377 assert_req_bump("1.0.0", "~1.0", None);
378 assert_req_bump("1.1.0", "~1.0", "~1.1");
379 assert_req_bump("1.1.1", "~1.0", "~1.1");
380 assert_req_bump("2.0.0", "~1.0", "~2.0");
381 }
382
383 #[test]
384 fn tilde_patch() {
385 assert_req_bump("1.0.0", "~1.0.0", None);
386 assert_req_bump("1.1.0", "~1.0.0", "~1.1.0");
387 assert_req_bump("1.1.1", "~1.0.0", "~1.1.1");
388 assert_req_bump("2.0.0", "~1.0.0", "~2.0.0");
389 }
390
391 #[test]
392 fn equal_major() {
393 assert_req_bump("1.0.0", "=1", None);
394 assert_req_bump("1.1.0", "=1", None);
395 assert_req_bump("2.0.0", "=1", "=2");
396 }
397
398 #[test]
399 fn equal_minor() {
400 assert_req_bump("1.0.0", "=1.0", None);
401 assert_req_bump("1.1.0", "=1.0", "=1.1");
402 assert_req_bump("1.1.1", "=1.0", "=1.1");
403 assert_req_bump("2.0.0", "=1.0", "=2.0");
404 }
405
406 #[test]
407 fn equal_patch() {
408 assert_req_bump("1.0.0", "=1.0.0", None);
409 assert_req_bump("1.1.0", "=1.0.0", "=1.1.0");
410 assert_req_bump("1.1.1", "=1.0.0", "=1.1.1");
411 assert_req_bump("2.0.0", "=1.0.0", "=2.0.0");
412 }
413 }
414}