1use super::parser::parse_version;
2use core::fmt;
3use std::cmp::Ordering;
4use std::fmt::{Display, Write};
5use std::str::FromStr;
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
8pub enum VersionState {
9 Alpha(String),
10 Beta(String),
11 ReleaseCandidate(String),
12 Release,
13}
14
15impl Default for VersionState {
16 fn default() -> Self {
17 Self::Release
18 }
19}
20
21impl fmt::Display for VersionState {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 Self::Alpha(v) => write!(f, "alpha{v}"),
25 Self::Beta(v) => write!(f, "beta{v}"),
26 Self::ReleaseCandidate(v) => write!(f, "rc{v}"),
27 Self::Release => write!(f, ""),
28 }
29 }
30}
31
32#[allow(unused)]
42pub enum VersionPart {
43 Major,
44 Minor,
45 Patch,
46 Pre,
47}
48
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
58pub struct Version {
59 pub major: usize,
60 pub minor: Option<usize>,
61 pub patch: Option<usize>,
62 pub pre: Option<VersionState>,
63}
64
65impl Version {
66 pub fn is_stable(&self) -> bool {
80 match &self.pre {
81 None => true,
82 Some(x) => matches!(x, VersionState::Release),
83 }
84 }
85
86 pub fn covers(&self, other: &Version) -> bool {
113 if self.major != other.major {
114 return false;
115 }
116
117 match self.minor {
118 Some(x) if x != other.minor.unwrap_or_default() => return false,
119 _ => {}
120 };
121
122 match self.patch {
123 Some(x) if x != other.patch.unwrap_or_default() => return false,
124 _ => {}
125 };
126
127 match &self.pre {
128 Some(x) if x != &other.pre.clone().unwrap_or_default() => return false,
129 _ => {}
130 };
131
132 true
133 }
134
135 pub fn strip_after(&self, part: VersionPart) -> Self {
154 let s = self.clone();
155 match part {
156 VersionPart::Major => Self {
157 minor: None,
158 patch: None,
159 pre: None,
160 ..s
161 },
162 VersionPart::Minor => Self {
163 patch: None,
164 pre: None,
165 ..s
166 },
167 VersionPart::Patch => Self { pre: None, ..s },
168 VersionPart::Pre => s,
169 }
170 }
171}
172
173impl FromStr for Version {
174 type Err = anyhow::Error;
175
176 fn from_str(s: &str) -> Result<Self, Self::Err> {
177 parse_version(s)
178 }
179}
180
181impl Ord for Version {
182 fn cmp(&self, other: &Self) -> Ordering {
183 if self == other {
184 return Ordering::Equal;
185 }
186
187 match self.major.cmp(&other.major) {
188 Ordering::Equal => {}
189 ord => return ord,
190 }
191
192 match self
193 .minor
194 .unwrap_or_default()
195 .cmp(&other.minor.unwrap_or_default())
196 {
197 Ordering::Equal => {}
198 ord => return ord,
199 }
200
201 match self
202 .patch
203 .unwrap_or_default()
204 .cmp(&other.patch.unwrap_or_default())
205 {
206 Ordering::Equal => {}
207 ord => return ord,
208 }
209
210 self.pre
211 .clone()
212 .unwrap_or_default()
213 .cmp(&other.pre.clone().unwrap_or_default())
214 }
215}
216
217impl PartialOrd for Version {
218 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
219 Some(self.cmp(other))
220 }
221}
222
223impl Display for Version {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 f.write_str(&self.major.to_string())?;
226
227 if let Some(minor) = &self.minor {
228 f.write_char('.')?;
229 f.write_str(&minor.to_string())?;
230 }
231
232 if let Some(patch) = &self.patch {
233 f.write_char('.')?;
234 f.write_str(&patch.to_string())?;
235 }
236
237 if let Some(pre) = &self.pre {
238 f.write_str(&pre.to_string())?;
239 }
240
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246mod test {
247 use super::*;
248
249 #[test]
250 fn parse_stable() {
251 assert_eq!(
252 Version::from_str("1").unwrap(),
253 Version {
254 major: 1,
255 ..Default::default()
256 }
257 );
258
259 assert_eq!(
260 Version::from_str("1.2").unwrap(),
261 Version {
262 major: 1,
263 minor: Some(2),
264 ..Default::default()
265 }
266 );
267
268 assert_eq!(
269 Version::from_str("1.2.345").unwrap(),
270 Version {
271 major: 1,
272 minor: Some(2),
273 patch: Some(345),
274 ..Default::default()
275 }
276 );
277
278 assert_eq!(
279 Version::from_str("v1.2.345").unwrap(),
280 Version {
281 major: 1,
282 minor: Some(2),
283 patch: Some(345),
284 ..Default::default()
285 }
286 );
287
288 assert_eq!(
289 Version::from_str("V1.2.345").unwrap(),
290 Version {
291 major: 1,
292 minor: Some(2),
293 patch: Some(345),
294 ..Default::default()
295 }
296 );
297 }
298
299 #[test]
300 fn parse_unstable() {
301 assert_eq!(
302 Version::from_str("1rc1").unwrap(),
303 Version {
304 major: 1,
305 pre: Some(VersionState::ReleaseCandidate("1".into())),
306 ..Default::default()
307 }
308 );
309
310 assert_eq!(
311 Version::from_str("1-rc1").unwrap(),
312 Version {
313 major: 1,
314 pre: Some(VersionState::ReleaseCandidate("1".into())),
315 ..Default::default()
316 }
317 );
318
319 assert_eq!(
320 Version::from_str("1-rc.1").unwrap(),
321 Version {
322 major: 1,
323 pre: Some(VersionState::ReleaseCandidate("1".into())),
324 ..Default::default()
325 }
326 );
327
328 assert_eq!(
329 Version::from_str("1.2beta34").unwrap(),
330 Version {
331 major: 1,
332 minor: Some(2),
333 pre: Some(VersionState::Beta("34".into())),
334 ..Default::default()
335 }
336 );
337
338 assert_eq!(
339 Version::from_str("1.2-beta34").unwrap(),
340 Version {
341 major: 1,
342 minor: Some(2),
343 pre: Some(VersionState::Beta("34".into())),
344 ..Default::default()
345 }
346 );
347
348 assert_eq!(
349 Version::from_str("1.2-beta.3.4").unwrap(),
350 Version {
351 major: 1,
352 minor: Some(2),
353 pre: Some(VersionState::Beta("3.4".into())),
354 ..Default::default()
355 }
356 );
357
358 assert_eq!(
359 Version::from_str("1.2.345alpha6.7.8").unwrap(),
360 Version {
361 major: 1,
362 minor: Some(2),
363 patch: Some(345),
364 pre: Some(VersionState::Alpha("6.7.8".into()))
365 }
366 );
367
368 assert_eq!(
369 Version::from_str("1.2.345-alpha6.7.8").unwrap(),
370 Version {
371 major: 1,
372 minor: Some(2),
373 patch: Some(345),
374 pre: Some(VersionState::Alpha("6.7.8".into()))
375 }
376 );
377
378 assert_eq!(
379 Version::from_str("1.2.345-alpha.6.7.8").unwrap(),
380 Version {
381 major: 1,
382 minor: Some(2),
383 patch: Some(345),
384 pre: Some(VersionState::Alpha("6.7.8".into()))
385 }
386 );
387 }
388
389 #[test]
390 fn ord() {
391 assert!(Version::from_str("2").unwrap() > Version::from_str("1").unwrap());
392 assert!(Version::from_str("2.1").unwrap() > Version::from_str("1.3").unwrap());
393 assert!(Version::from_str("1.4").unwrap() > Version::from_str("1.3").unwrap());
394 assert!(Version::from_str("1.2.3").unwrap() > Version::from_str("1.2").unwrap());
395 assert!(Version::from_str("1.2.3").unwrap() > Version::from_str("1.2.2").unwrap());
396 assert!(Version::from_str("1").unwrap() > Version::from_str("1rc1").unwrap());
397 assert!(Version::from_str("1.2rc1").unwrap() > Version::from_str("1.2beta1").unwrap());
398 assert!(Version::from_str("1.2beta2").unwrap() > Version::from_str("1.2beta1").unwrap());
399 assert!(Version::from_str("2").unwrap() > Version::from_str("1rc2").unwrap());
400 }
401
402 #[test]
403 fn is_stable() {
404 assert!(Version::from_str("1").unwrap().is_stable());
405 assert!(Version::from_str("1.1").unwrap().is_stable());
406 assert!(Version::from_str("1.1.3").unwrap().is_stable());
407
408 assert!(!Version::from_str("1alpha1").unwrap().is_stable());
409 assert!(!Version::from_str("1.2beta2").unwrap().is_stable());
410 assert!(!Version::from_str("1.2.3rc3").unwrap().is_stable());
411 }
412
413 #[test]
414 fn covers() {
415 let a: Version = "1.2".parse().unwrap();
416 let b: Version = "1.2".parse().unwrap();
417 assert!(a.covers(&b));
418
419 let a: Version = "1".parse().unwrap();
420 let b: Version = "1.2.1".parse().unwrap();
421 assert!(a.covers(&b));
422
423 let a: Version = "1.3".parse().unwrap();
424 let b: Version = "1.3.7".parse().unwrap();
425 assert!(a.covers(&b));
426
427 let a: Version = "1.2.3".parse().unwrap();
428 let b: Version = "1.2.3rc1".parse().unwrap();
429 assert!(a.covers(&b));
430
431 let a: Version = "1.3".parse().unwrap();
432 let b: Version = "1.2.1".parse().unwrap();
433 assert!(!a.covers(&b));
434 }
435
436 #[test]
437 fn strip_after() {
438 let a: Version = "1.2.3rc1".parse().unwrap();
439 let b: Version = "1".parse().unwrap();
440 assert_eq!(a.strip_after(VersionPart::Major), b);
441
442 let a: Version = "1.2.3rc1".parse().unwrap();
443 let b: Version = "1.2".parse().unwrap();
444 assert_eq!(a.strip_after(VersionPart::Minor), b);
445
446 let a: Version = "1.2.3rc1".parse().unwrap();
447 let b: Version = "1.2.3".parse().unwrap();
448 assert_eq!(a.strip_after(VersionPart::Patch), b);
449
450 let a: Version = "1.2.3rc1".parse().unwrap();
451 let b: Version = "1.2.3rc1".parse().unwrap();
452 assert_eq!(a.strip_after(VersionPart::Pre), b);
453 }
454}