1use std::cmp::Ordering;
5
6pub fn parse_semver(version: &str) -> Option<Semver> {
8 let version = version.trim();
9
10 let version = version.strip_prefix('v').unwrap_or(version);
12
13 let parts: Vec<&str> = version.split('.').collect();
14
15 if parts.is_empty() {
16 return None;
17 }
18
19 let major = parts[0].parse().ok()?;
20 let minor = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
21 let patch = parts
22 .get(2)
23 .and_then(|s| s.split('-').next()?.parse().ok())
24 .unwrap_or(0);
25
26 Some(Semver {
27 major,
28 minor,
29 patch,
30 })
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct Semver {
36 pub major: u32,
37 pub minor: u32,
38 pub patch: u32,
39}
40
41impl Semver {
42 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
43 Self {
44 major,
45 minor,
46 patch,
47 }
48 }
49
50 pub fn compare(&self, other: &Semver) -> Ordering {
52 self.major
53 .cmp(&other.major)
54 .then(self.minor.cmp(&other.minor))
55 .then(self.patch.cmp(&other.patch))
56 }
57
58 pub fn is_compatible(&self, other: &Semver) -> bool {
60 self.major == other.major
61 }
62
63 pub fn gt(&self, other: &Semver) -> bool {
65 self.compare(other) == Ordering::Greater
66 }
67
68 pub fn lt(&self, other: &Semver) -> bool {
70 self.compare(other) == Ordering::Less
71 }
72
73 pub fn eq(&self, other: &Semver) -> bool {
75 self.compare(other) == Ordering::Equal
76 }
77}
78
79impl std::fmt::Display for Semver {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
82 }
83}
84
85impl PartialOrd for Semver {
86 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
87 Some(self.compare(other))
88 }
89}
90
91impl Ord for Semver {
92 fn cmp(&self, other: &Self) -> Ordering {
93 self.compare(other)
94 }
95}