1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Version {
12 pub major: u32,
13 pub minor: u32,
14 pub patch: u32,
15}
16
17impl Version {
18 pub fn parse(s: &str) -> Option<Self> {
21 let mut parts = s.split('.');
22 let major = parts.next()?.parse().ok()?;
23 let minor = parts.next()?.parse().ok()?;
24 let patch = parts.next()?.parse().ok()?;
25 if parts.next().is_some() {
26 return None; }
28 Some(Self {
29 major,
30 minor,
31 patch,
32 })
33 }
34}
35
36pub fn current() -> Version {
38 Version::parse(crate::VERSION).expect("CARGO_PKG_VERSION is not valid semver")
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn parse_valid() {
47 assert_eq!(
48 Version::parse("1.2.3"),
49 Some(Version {
50 major: 1,
51 minor: 2,
52 patch: 3,
53 })
54 );
55 }
56
57 #[test]
58 fn parse_zeros() {
59 assert_eq!(
60 Version::parse("0.0.0"),
61 Some(Version {
62 major: 0,
63 minor: 0,
64 patch: 0,
65 })
66 );
67 }
68
69 #[test]
70 fn parse_large_numbers() {
71 assert_eq!(
72 Version::parse("100.200.300"),
73 Some(Version {
74 major: 100,
75 minor: 200,
76 patch: 300,
77 })
78 );
79 }
80
81 #[test]
82 fn parse_rejects_empty() {
83 assert_eq!(Version::parse(""), None);
84 }
85
86 #[test]
87 fn parse_rejects_one_component() {
88 assert_eq!(Version::parse("1"), None);
89 }
90
91 #[test]
92 fn parse_rejects_two_components() {
93 assert_eq!(Version::parse("1.2"), None);
94 }
95
96 #[test]
97 fn parse_rejects_four_components() {
98 assert_eq!(Version::parse("1.2.3.4"), None);
99 }
100
101 #[test]
102 fn parse_rejects_non_numeric() {
103 assert_eq!(Version::parse("1.2.beta"), None);
104 }
105
106 #[test]
107 fn parse_rejects_negative() {
108 assert_eq!(Version::parse("1.-2.3"), None);
109 }
110
111 #[test]
112 fn ordering_by_patch() {
113 let v1 = Version::parse("1.0.1").unwrap();
114 let v2 = Version::parse("1.0.2").unwrap();
115 assert!(v1 < v2);
116 }
117
118 #[test]
119 fn ordering_by_minor() {
120 let v1 = Version::parse("1.1.9").unwrap();
121 let v2 = Version::parse("1.2.0").unwrap();
122 assert!(v1 < v2);
123 }
124
125 #[test]
126 fn ordering_by_major() {
127 let v1 = Version::parse("1.9.9").unwrap();
128 let v2 = Version::parse("2.0.0").unwrap();
129 assert!(v1 < v2);
130 }
131
132 #[test]
133 fn ordering_equal() {
134 let v1 = Version::parse("1.2.3").unwrap();
135 let v2 = Version::parse("1.2.3").unwrap();
136 assert_eq!(v1, v2);
137 assert!(v1 >= v2);
138 assert!(v1 <= v2);
139 }
140
141 #[test]
142 fn current_version_parses() {
143 let v = current();
144 assert!(v.major > 0 || v.minor > 0 || v.patch > 0);
146 }
147}