schema_model/model/
version.rs1use std::cmp::Ordering;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
5pub struct Version {
6 major_version: i32,
7 minor_version: i32,
8 patch_version: i32,
9 pre_release_suffix: bool,
10}
11
12impl Version {
13 pub fn new(major_version: i32, minor_version: i32) -> Self {
14 Self {
15 major_version,
16 minor_version,
17 patch_version: 0,
18 pre_release_suffix: false,
19 }
20 }
21
22 pub fn with_patch(major_version: i32, minor_version: i32, patch_version: i32) -> Self {
23 Self {
24 major_version,
25 minor_version,
26 patch_version,
27 pre_release_suffix: false,
28 }
29 }
30
31 pub fn with_patch_and_suffix(
32 major_version: i32,
33 minor_version: i32,
34 patch_version: i32,
35 pre_release_suffix: bool,
36 ) -> Self {
37 Self {
38 major_version,
39 minor_version,
40 patch_version,
41 pre_release_suffix,
42 }
43 }
44
45 pub fn parse(version_str: &str) -> Self {
46 let mut s = version_str.trim();
48 let pre = s.contains("-SNAPSHOT");
49 if pre {
50 if let Some(idx) = s.find("-SNAPSHOT") {
51 s = &s[..idx];
52 }
53 }
54 let parts: Vec<&str> = s.split('.').collect();
55 let major = parts
56 .get(0)
57 .and_then(|p| p.parse::<i32>().ok())
58 .unwrap_or(0);
59 let minor = parts
60 .get(1)
61 .and_then(|p| p.parse::<i32>().ok())
62 .unwrap_or(0);
63 let patch = parts
64 .get(2)
65 .and_then(|p| p.parse::<i32>().ok())
66 .unwrap_or(0);
67 Self {
68 major_version: major,
69 minor_version: minor,
70 patch_version: patch,
71 pre_release_suffix: pre,
72 }
73 }
74
75 pub fn major_version(&self) -> i32 {
76 self.major_version
77 }
78 pub fn minor_version(&self) -> i32 {
79 self.minor_version
80 }
81 pub fn patch_version(&self) -> i32 {
82 self.patch_version
83 }
84 pub fn is_pre_release_suffix(&self) -> bool {
85 self.pre_release_suffix
86 }
87}
88
89impl fmt::Display for Version {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 if self.pre_release_suffix {
92 if self.patch_version < 1 {
93 write!(
94 f,
95 "{:02}.{:02}-SNAPSHOT",
96 self.major_version, self.minor_version
97 )
98 } else {
99 write!(
100 f,
101 "{:02}.{:02}.{:02}-SNAPSHOT",
102 self.major_version, self.minor_version, self.patch_version
103 )
104 }
105 } else if self.patch_version < 1 {
106 write!(f, "{:02}.{:02}", self.major_version, self.minor_version)
107 } else {
108 write!(
109 f,
110 "{:02}.{:02}.{:02}",
111 self.major_version, self.minor_version, self.patch_version
112 )
113 }
114 }
115}
116
117impl PartialOrd for Version {
118 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
119 Some(self.cmp(other))
120 }
121}
122
123impl Ord for Version {
124 fn cmp(&self, other: &Self) -> Ordering {
125 if std::ptr::eq(self, other) {
126 return Ordering::Equal;
127 }
128 match self.major_version.cmp(&other.major_version) {
129 Ordering::Equal => {}
130 ord => return ord,
131 }
132 match self.minor_version.cmp(&other.minor_version) {
133 Ordering::Equal => {}
134 ord => return ord,
135 }
136 match self.patch_version.cmp(&other.patch_version) {
137 Ordering::Equal => {}
138 ord => return ord,
139 }
140 match (self.pre_release_suffix, other.pre_release_suffix) {
141 (true, false) => Ordering::Greater,
142 (false, true) => Ordering::Less,
143 _ => Ordering::Equal,
144 }
145 }
146}
147
148impl From<&str> for Version {
149 fn from(value: &str) -> Self {
150 Version::parse(value)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn parse_and_display() {
160 let v = Version::parse("1.2");
161 assert_eq!(v.major_version(), 1);
162 assert_eq!(v.minor_version(), 2);
163 assert_eq!(v.patch_version(), 0);
164 assert!(!v.is_pre_release_suffix());
165 assert_eq!(v.to_string(), "01.02");
166
167 let v2 = Version::parse("1.2.3-SNAPSHOT");
168 assert_eq!(v2.to_string(), "01.02.03-SNAPSHOT");
169 assert!(v2.is_pre_release_suffix());
170 }
171
172 #[test]
173 fn ordering_matches_java_logic() {
174 let a = Version::with_patch(1, 2, 0);
175 let b = Version::with_patch(1, 2, 1);
176 assert!(a < b);
177
178 let c = Version::with_patch_and_suffix(1, 2, 3, true);
179 let d = Version::with_patch_and_suffix(1, 2, 3, false);
180 assert!(c > d);
182 }
183}