1use std::cmp::Ordering;
2use std::fmt::{Debug, Display};
3
4pub struct Version {
16 pub epoch: Option<u64>, pub components: Vec<String>, pub pre_release: Option<String>, pub build_metadata: Option<String>, }
21impl Version {
22 pub fn parse(version_str: &str) -> Self {
30 let mut parts = version_str.splitn(2, ':');
32 let epoch = parts.next().and_then(|s| s.parse::<u64>().ok());
33 let rest = parts.next().unwrap_or(version_str);
34
35 let mut parts = rest.splitn(2, '+');
37 let version_part = parts.next().unwrap_or(rest);
38 let build_metadata = parts.next().map(|s| s.to_string());
39
40 let mut parts = version_part.splitn(2, '-');
42 let main_version = parts.next().unwrap_or(version_part);
43 let pre_release = parts.next().map(|s| s.to_string());
44
45 let components: Vec<String> = main_version
47 .split(['.']) .map(|s| s.to_string())
49 .collect();
50
51 Version {
52 epoch,
53 components,
54 pre_release,
55 build_metadata,
56 }
57 }
58}
59impl PartialEq for Version {
60 fn eq(&self, other: &Self) -> bool {
61 self.epoch == other.epoch
62 && self.components == other.components
63 && self.pre_release == other.pre_release
64 && self.build_metadata == other.build_metadata
65 }
66 fn ne(&self, other: &Self) -> bool {
67 self.epoch != other.epoch
68 || self.components != other.components
69 || self.pre_release != other.pre_release
70 || self.build_metadata != other.build_metadata
71 }
72}
73impl Eq for Version {}
74
75impl PartialOrd for Version {
76 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
77 Some(self.cmp(other))
78 }
79}
80impl Ord for Version {
81 fn cmp(&self, other: &Self) -> Ordering {
82 if let Some(epoch_cmp) = self.epoch.partial_cmp(&other.epoch) {
84 if epoch_cmp != Ordering::Equal {
85 return epoch_cmp;
86 }
87 }
88
89 for (a, b) in self.components.iter().zip(&other.components) {
91 let a_num = a.parse::<u64>().ok();
92 let b_num = b.parse::<u64>().ok();
93
94 match (a_num, b_num) {
95 (Some(a_num), Some(b_num)) => {
97 let cmp = a_num.cmp(&b_num);
98 if cmp != Ordering::Equal {
99 return cmp;
100 }
101 }
102 (Some(_), None) => return Ordering::Less,
104 (None, Some(_)) => return Ordering::Greater,
105 (None, None) => {
107 let cmp = a.cmp(b);
108 if cmp != Ordering::Equal {
109 return cmp;
110 }
111 }
112 }
113 }
114 if self.pre_release.is_some() || other.pre_release.is_some() {
116 return match (&self.pre_release, &other.pre_release) {
117 (None, None) => Ordering::Equal,
118 (None, Some(_)) => Ordering::Greater, (Some(_), None) => Ordering::Less, (Some(a), Some(b)) => a.to_lowercase().cmp(&b.to_lowercase()), }
122 }
123 self.build_metadata.partial_cmp(&other.build_metadata).unwrap()
125 }
126}
127impl Debug for Version {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 let epoch = self.epoch.unwrap_or_default();
130 let components = &self.components;
131 let pre_release = self.pre_release.clone().unwrap_or_default();
132 let build_metadata = self.epoch.unwrap_or_default();
133 write!(f, "epoch:{epoch} components:{components:?} pre_release:{pre_release} build_metadata:{build_metadata}")
134 }
135}
136impl Display for Version {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 let mut string = String::new();
139 let epoch = self.epoch;
141 if let Some(epoch) = epoch {
142 string += epoch.to_string().as_str();
143 string += ":";
144 }
145 let components = &self.components;
147 for component in components {
148 string += component.as_str();
149 string += ".";
150 }
151 string.remove(string.len() - 1);
152 let pre_release = self.pre_release.clone();
154 if let Some(pre_release) = pre_release {
155 string += "-";
156 string += pre_release.as_str();
157 }
158 let build_metadata = self.build_metadata.clone();
159 if let Some(build_metadata) = build_metadata {
160 string += "+";
161 string += build_metadata.as_str();
162 }
163 write!(f, "{}",string)
164 }
165}
166impl Default for Version {
167 fn default() -> Self {
168 Version::parse("0.0.1")
169 }
170}