alfred_workflow_rust_project/
version.rs

1use serde::{Deserialize, Serialize};
2
3// version eg: v1.2.3-RC
4#[derive(Serialize, Deserialize, PartialEq)]
5pub struct Version {
6    major: u8,
7    minor: u8,
8    patch: u8,
9    build: Option<String>,
10}
11
12const SUFFIX_CANDIDATE: [&str; 2] = ["M", "RC"];
13
14impl Version {
15    pub fn new(version_str: &str) -> Result<Version, &'static str> {
16        let vp: Vec<&str> = version_str.split(".").collect();
17        if vp.len() != 3 {
18            return Err("version should be like 1.0.0 or 1.2.3 or 1.2.3-RC");
19        }
20
21        let path_with_build = vp.get(2).unwrap();
22        let vb: Vec<&str> = path_with_build.split("-").collect();
23        if vb.len() > 2 {
24            return Err("version should be like 1.0.0 or 1.2.3 or 1.2.3-RC");
25        }
26
27        let major_with_prefix = vp.get(0).unwrap();
28        let mut major_str = major_with_prefix.to_string();
29        if major_str.starts_with("v") {
30            major_str = major_str.to_uppercase().replace("V", "");
31        }
32
33        let version = Version {
34            major: major_str.parse().unwrap(),
35            minor: vp.get(1).unwrap().parse().unwrap(),
36            patch: vb.get(0).unwrap().parse().unwrap(),
37            build: if vb.len() == 2 {
38                Some(vb.get(1).unwrap().to_string())
39            } else {
40                None
41            },
42        };
43
44        if version.build.is_some() {
45            let suffix = version.build.as_ref().unwrap();
46            let mut build_valid = false;
47            for suf in SUFFIX_CANDIDATE {
48                if suffix.starts_with(suf) {
49                    build_valid = true;
50                }
51            }
52            if !build_valid {
53                return Err("Version suffix should be like RC1 or RC2");
54            }
55        }
56        Ok(version)
57    }
58
59    fn compare(&self, comparison: &Version) -> i8 {
60        let self_v_num = self.major * 100 + self.minor * 10 + self.patch;
61        let comp_v_num = comparison.major * 100 + comparison.minor * 10 + comparison.patch;
62        if self_v_num > comp_v_num {
63            return 1;
64        }
65
66        if self_v_num < comp_v_num {
67            return -1;
68        }
69
70        // must compare the build
71        if self.build.is_some() && comparison.build.is_none() {
72            return -1;
73        }
74
75        if self.build.is_none() && comparison.build.is_some() {
76            return 1;
77        }
78
79        if self.build.is_none() && comparison.build.is_none() {
80            return 0;
81        }
82
83        let self_build_v: u8 = self
84            .build
85            .as_ref()
86            .unwrap()
87            .replace("RC", "0")
88            .parse()
89            .unwrap();
90        let comparison_build_v: u8 = comparison
91            .build
92            .as_ref()
93            .unwrap()
94            .replace("RC", "0")
95            .parse()
96            .unwrap();
97
98        return if self_build_v == comparison_build_v {
99            0
100        } else if self_build_v > comparison_build_v {
101            1
102        } else {
103            -1
104        };
105    }
106
107    pub fn gt(&self, com: &Version) -> bool {
108        if self.compare(com) == 1 {
109            true
110        } else {
111            false
112        }
113    }
114
115    pub fn eq(&self, com: &Version) -> bool {
116        if self.compare(com) == 0 {
117            true
118        } else {
119            false
120        }
121    }
122
123    pub fn lt(&self, com: &Version) -> bool {
124        !self.gt(com)
125    }
126}
127
128#[cfg(test)]
129mod version_unit_test {
130    use crate::version::Version;
131
132    #[test]
133    fn test_parse_version() {
134        let v = Version::new("1.2.3").unwrap();
135        assert_eq!(v.major, 1);
136        assert_eq!(v.minor, 2);
137        assert_eq!(v.patch, 3);
138    }
139
140    #[test]
141    fn test_parse_version_with_invalid() {
142        let v = Version::new("r1.2.3");
143        assert_eq!(v.is_err(), true);
144
145        let v2 = Version::new("1.2.3.RC");
146        assert_eq!(v2.is_err(), true);
147
148        let v3 = Version::new("1.2.3-RCX");
149        assert_eq!(v3.is_err(), true);
150
151        let v4 = Version::new("1.2.3.4-RC");
152        assert_eq!(v4.is_err(), true);
153    }
154
155    #[test]
156    fn test_parse_version_with_build() {
157        let v = Version::new("1.2.3-RC").unwrap();
158        assert_eq!(v.major, 1);
159        assert_eq!(v.minor, 2);
160        assert_eq!(v.patch, 3);
161        assert_eq!(v.build, Some("RC".to_string()));
162    }
163
164    #[test]
165    fn test_parse_version_with_prefix() {
166        let v = Version::new("v1.2.3").unwrap();
167        assert_eq!(v.major, 1);
168        assert_eq!(v.minor, 2);
169        assert_eq!(v.patch, 3);
170    }
171
172    #[test]
173    fn test_parse_version_with_prefix_build() {
174        let v = Version::new("v1.2.3-RC").unwrap();
175        assert_eq!(v.major, 1);
176        assert_eq!(v.minor, 2);
177        assert_eq!(v.patch, 3);
178        assert_eq!(v.build, Some("RC".to_string()))
179    }
180
181    #[test]
182    fn test_version_newer() {
183        let v = Version::new("1.2.3").unwrap();
184        let v2 = Version::new("2.2.3").unwrap();
185        assert_eq!(v.compare(&v2), -1);
186
187        let v3 = Version::new("1.3.3").unwrap();
188        assert_eq!(v.compare(&v3), -1);
189
190        let v4 = Version::new("1.2.4").unwrap();
191        assert_eq!(v.compare(&v4), -1);
192    }
193
194    #[test]
195    fn test_version_newer_with_prefix() {
196        let v = Version::new("v1.2.3").unwrap();
197        let v2 = Version::new("v2.2.3").unwrap();
198        assert_eq!(v.compare(&v2), -1);
199
200        let v3 = Version::new("v1.3.3").unwrap();
201        assert_eq!(v.compare(&v3), -1);
202
203        let v4 = Version::new("1.2.4").unwrap();
204        assert_eq!(v.compare(&v4), -1);
205    }
206
207    #[test]
208    fn test_version_newer_with_build() {
209        let v = Version::new("v1.2.3").unwrap();
210        let v2 = Version::new("v2.2.3").unwrap();
211        assert_eq!(v.compare(&v2), -1);
212
213        let v3 = Version::new("v1.3.3").unwrap();
214        assert_eq!(v.compare(&v3), -1);
215
216        let v4 = Version::new("1.2.4").unwrap();
217        assert_eq!(v.compare(&v4), -1);
218
219        let v5 = Version::new("1.2.3-RC").unwrap();
220        assert_eq!(v.compare(&v5), 1);
221
222        let v6 = Version::new("1.2.3-RC1").unwrap();
223        assert_eq!(v6.compare(&v5), 1);
224
225        let v7 = Version::new("1.2.3-RC2").unwrap();
226        assert_eq!(v7.compare(&v6), 1);
227    }
228
229    #[test]
230    fn test_version_equal() {
231        let _v = Version::new("1.2.3").unwrap();
232        let _v1 = Version::new("v1.2.3").unwrap();
233    }
234}