1use anyhow::Result;
11use serde::{Deserialize, Deserializer, Serialize};
12use tracing::error;
13use winnow::Parser;
14use winnow::ascii::digit1;
15use winnow::combinator::{alt, opt, preceded, seq};
16use winnow::error::{ContextError, ErrMode};
17use winnow::prelude::*;
18
19use std::fmt::{self, Display};
20use std::str::FromStr;
21
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
28pub struct Version {
29 pub major: u32,
31 pub minor: u32,
33 pub patch: u32,
35 pub build: u32,
37}
38
39impl FromStr for Version {
57 type Err = anyhow::Error;
58
59 fn from_str(s: &str) -> Result<Self> {
60 if s.is_empty() {
61 return Err(anyhow::anyhow!("Version string cannot be empty"));
62 }
63 let version = Version::parse_version(s)
64 .map_err(|e| anyhow::anyhow!("Failed to parse version: {}", e))?;
65 Ok(version)
66 }
67}
68
69impl Version {
70 pub fn new(major: u32, minor: u32, patch: u32, build: Option<u32>) -> Self {
72 match build {
73 Some(build) => Self {
74 major,
75 minor,
76 patch,
77 build,
78 },
79 None => Self::new_without_build(major, minor, patch),
80 }
81 }
82 pub fn new_without_build(major: u32, minor: u32, patch: u32) -> Self {
83 Self {
84 major,
85 minor,
86 patch,
87 ..Default::default()
88 }
89 }
90
91 fn parse_version(input: &str) -> ModalResult<Version> {
93 let mut input_slice = input;
94
95 let (_, major, minor, patch, build) = seq!(
96 opt(alt(("v", "V"))),
97 digit1.parse_to::<u32>(),
98 preceded('.', digit1.parse_to::<u32>()),
99 preceded('.', digit1.parse_to::<u32>()),
100 opt(preceded('.', digit1.parse_to::<u32>())),
101 )
102 .parse_next(&mut input_slice)?;
103
104 if !input_slice.is_empty() {
106 let error_msg = format!(
108 "Invalid version format: extra characters '{}' found after '{}'",
109 input_slice,
110 &input[..input.len() - input_slice.len()]
111 );
112 error!("{}", error_msg);
113 return Err(ErrMode::Cut(ContextError::default()));
114 }
115
116 Ok(Version::new(major, minor, patch, build))
117 }
118
119 pub fn base_version(&self) -> Version {
131 Version::new_without_build(self.major, self.minor, self.patch)
132 }
133
134 pub fn can_apply_patch(&self, patch_base_version: &Version) -> bool {
151 self.base_version() == patch_base_version.base_version()
152 }
153
154 pub fn is_compatible_with_patch(&self, patch_version: &Version) -> bool {
159 self.base_version() == patch_version.base_version() && self.build <= patch_version.build
160 }
161
162 pub fn to_short_string(&self) -> String {
168 if self.build == 0 {
169 format!("{}.{}.{}", self.major, self.minor, self.patch)
170 } else {
171 self.to_string()
172 }
173 }
174
175 pub fn base_version_string(&self) -> String {
177 format!("{}.{}.{}", self.major, self.minor, self.patch)
178 }
179
180 pub fn validate(&self) -> Result<()> {
182 if self.major > 999 || self.minor > 999 || self.patch > 999 || self.build > 9999 {
184 return Err(anyhow::anyhow!(
185 "Version number values are too large and may be invalid"
186 ));
187 }
188
189 Ok(())
190 }
191}
192
193impl Display for Version {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 write!(
196 f,
197 "{}.{}.{}.{}",
198 self.major, self.minor, self.patch, self.build
199 )
200 }
201}
202
203pub fn version_from_str<'de, D>(deserializer: D) -> std::result::Result<Version, D::Error>
205where
206 D: Deserializer<'de>,
207{
208 let s = String::deserialize(deserializer)?;
209 Version::from_str(&s).map_err(serde::de::Error::custom)
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
214pub enum VersionComparison {
215 Equal,
217 Newer,
219 PatchUpgradeable,
221 FullUpgradeRequired,
223}
224
225impl Version {
226 pub fn compare_detailed(&self, server_version: &Version) -> VersionComparison {
243 if self == server_version {
244 return VersionComparison::Equal;
245 }
246
247 if self.can_apply_patch(server_version) {
249 if self.build < server_version.build {
251 VersionComparison::PatchUpgradeable
252 } else {
253 VersionComparison::Newer
254 }
255 } else {
256 let self_base = self.base_version();
258 let server_base = server_version.base_version();
259 if self_base < server_base {
260 VersionComparison::FullUpgradeRequired
261 } else {
262 VersionComparison::Newer
263 }
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_version_parsing() {
274 let v1 = Version::from_str("0.0.13.5").unwrap();
276 assert_eq!(v1.major, 0);
277 assert_eq!(v1.minor, 0);
278 assert_eq!(v1.patch, 13);
279 assert_eq!(v1.build, 5);
280
281 let v2 = Version::from_str("1.2.3").unwrap();
283 assert_eq!(v2.major, 1);
284 assert_eq!(v2.minor, 2);
285 assert_eq!(v2.patch, 3);
286 assert_eq!(v2.build, 0);
287
288 assert!(Version::from_str("1.2").is_err());
290 assert!(Version::from_str("1.2.3.4.5").is_err());
291 assert!(Version::from_str("").is_err());
292 assert!(Version::from_str("a.b.c").is_err());
293 }
294
295 #[test]
296 fn test_version_comparison() {
297 let v1 = Version::from_str("0.0.13.5").unwrap();
298 let v2 = Version::from_str("0.0.13.2").unwrap();
299 let v3 = Version::from_str("0.0.14.0").unwrap();
300
301 assert!(v1 > v2);
303 assert!(v2 < v1);
304 assert!(v3 > v1);
305 assert!(v3 > v2);
306
307 let v4 = Version::from_str("0.0.13.5").unwrap();
309 assert_eq!(v1, v4);
310 }
311
312 #[test]
313 fn test_base_version() {
314 let v1 = Version::from_str("0.0.13.5").unwrap();
315 let base = v1.base_version();
316
317 assert_eq!(base.major, 0);
318 assert_eq!(base.minor, 0);
319 assert_eq!(base.patch, 13);
320 assert_eq!(base.build, 0);
321 assert_eq!(base.to_string(), "0.0.13.0");
322 }
323
324 #[test]
325 fn test_can_apply_patch() {
326 let current = Version::from_str("0.0.13.2").unwrap();
327 let patch_target = Version::from_str("0.0.13.0").unwrap();
328 let different_base = Version::from_str("0.0.14.0").unwrap();
329
330 assert!(current.can_apply_patch(&patch_target));
331 assert!(!current.can_apply_patch(&different_base));
332 }
333
334 #[test]
335 fn test_version_display() {
336 let v = Version::from_str("0.0.13.5").unwrap();
337 assert_eq!(v.to_string(), "0.0.13.5");
338
339 let v_short = Version::from_str("0.0.13.0").unwrap();
340 assert_eq!(v_short.to_short_string(), "0.0.13");
341 assert_eq!(v.to_short_string(), "0.0.13.5");
342 }
343
344 #[test]
345 fn test_detailed_comparison() {
346 let current = Version::from_str("0.0.13.2").unwrap();
347
348 let same = Version::from_str("0.0.13.2").unwrap();
350 assert_eq!(current.compare_detailed(&same), VersionComparison::Equal);
351
352 let patch_upgrade = Version::from_str("0.0.13.5").unwrap();
354 assert_eq!(
355 current.compare_detailed(&patch_upgrade),
356 VersionComparison::PatchUpgradeable
357 );
358
359 let full_upgrade = Version::from_str("0.0.14.0").unwrap();
361 assert_eq!(
362 current.compare_detailed(&full_upgrade),
363 VersionComparison::FullUpgradeRequired
364 );
365
366 let older = Version::from_str("0.0.12.0").unwrap();
368 assert_eq!(current.compare_detailed(&older), VersionComparison::Newer);
369 }
370
371 #[test]
372 fn test_compatibility() {
373 let current = Version::from_str("0.0.13.2").unwrap();
374 let patch_v1 = Version::from_str("0.0.13.5").unwrap();
375 let patch_v2 = Version::from_str("0.0.13.1").unwrap();
376 let different_base = Version::from_str("0.0.14.0").unwrap();
377
378 assert!(current.is_compatible_with_patch(&patch_v1));
379 assert!(!current.is_compatible_with_patch(&patch_v2)); assert!(!current.is_compatible_with_patch(&different_base));
381 }
382
383 #[test]
384 fn test_validation() {
385 let valid_v = Version::from_str("0.0.13.5").unwrap();
386 assert!(valid_v.validate().is_ok());
387
388 let invalid_v = Version::new(1000, 1000, 1000, Some(10000));
389 assert!(invalid_v.validate().is_err());
390 }
391
392 #[test]
394 fn test_task_1_2_acceptance_criteria() {
395 let v1 = Version::from_str("0.0.13.5").expect("应该能解析四段式版本号");
397 let v2 = Version::from_str("0.0.13.2").expect("应该能解析四段式版本号");
398
399 assert!(v1 > v2, "版本比较应该正常工作");
401
402 assert_eq!(v1.base_version(), v2.base_version(), "基础版本应该相同");
404
405 assert!(v1.can_apply_patch(&v2), "补丁适用性检查应该正常工作");
407
408 println!("✅ Task 1.2: 版本管理系统重构 - 验收标准测试通过");
409 println!(" - ✅ 四段式版本号解析功能正常");
410 println!(" - ✅ 版本比较逻辑正常工作");
411 println!(" - ✅ 基础版本提取功能正常");
412 println!(" - ✅ 补丁适用性检查功能正常");
413 println!(" - ✅ 版本格式验证功能正常");
414 }
415}