Skip to main content

client_core/
version.rs

1//! # 版本管理模块
2//!
3//! 提供统一的版本号解析、比较和管理功能,支持:
4//! - 四段式版本号格式 (major.minor.patch.build)
5//! - 版本比较和排序
6//! - 基础版本提取
7//! - 补丁适用性检查
8//! - 版本格式验证
9
10use 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/// 版本号结构体,支持四段式版本号 (major.minor.patch.build)
23///
24/// # 示例
25/// - `0.0.13.0` - 基础版本 0.0.13,build level 0
26/// - `0.0.13.5` - 基础版本 0.0.13,build level 5 (应用了5个补丁)
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
28pub struct Version {
29    /// 主版本号
30    pub major: u32,
31    /// 次版本号
32    pub minor: u32,
33    /// 修订版本号
34    pub patch: u32,
35    /// 构建号/补丁级别
36    pub build: u32,
37}
38
39/// 从字符串解析版本号
40///
41/// 支持的格式:
42/// - "1.2.3" -> Version { major: 1, minor: 2, patch: 3, build: 0 }
43/// - "1.2.3.4" -> Version { major: 1, minor: 2, patch: 3, build: 4 }
44///
45/// # 示例
46/// ```
47/// use client_core::version::Version;
48/// use std::str::FromStr;
49///
50/// let v1 = Version::from_str("0.0.13.5").unwrap();
51/// assert_eq!(v1.major, 0);
52/// assert_eq!(v1.minor, 0);
53/// assert_eq!(v1.patch, 13);
54/// assert_eq!(v1.build, 5);
55/// ```
56impl 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    /// 创建新的版本号
71    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    /// 解析版本号,比如"v0.0.13.5","v0.1.2"
92    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        // 检查是否完全消耗输入
105        if !input_slice.is_empty() {
106            // 创建一个简单的错误,包含额外的字符信息
107            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    /// 获取基础版本(不包含build级别)
120    ///
121    /// # 示例
122    /// ```
123    /// use client_core::version::Version;
124    /// use std::str::FromStr;
125    ///
126    /// let v = Version::from_str("0.0.13.5").unwrap();
127    /// let base = v.base_version();
128    /// assert_eq!(base.to_string(), "0.0.13.0");
129    /// ```
130    pub fn base_version(&self) -> Version {
131        Version::new_without_build(self.major, self.minor, self.patch)
132    }
133
134    /// 检查是否可以在当前版本上应用指定的补丁
135    ///
136    /// 补丁只能应用在相同的基础版本上
137    ///
138    /// # 示例
139    /// ```
140    /// use client_core::version::Version;
141    /// use std::str::FromStr;
142    ///
143    /// let current = Version::from_str("0.0.13.2").unwrap();
144    /// let patch_target = Version::from_str("0.0.13.0").unwrap();
145    /// assert!(current.can_apply_patch(&patch_target));
146    ///
147    /// let different_base = Version::from_str("0.0.14.0").unwrap();
148    /// assert!(!current.can_apply_patch(&different_base));
149    /// ```
150    pub fn can_apply_patch(&self, patch_base_version: &Version) -> bool {
151        self.base_version() == patch_base_version.base_version()
152    }
153
154    /// 检查当前版本是否兼容指定的补丁版本
155    ///
156    /// 当前版本的基础版本必须与补丁版本的基础版本相同,
157    /// 且当前的build级别不能超过补丁版本
158    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    /// 获取版本字符串的简短表示(不包含build为0的情况)
163    ///
164    /// # 示例
165    /// - Version(0, 0, 13, 0) -> "0.0.13"
166    /// - Version(0, 0, 13, 5) -> "0.0.13.5"
167    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    /// 获取基础版本字符串
176    pub fn base_version_string(&self) -> String {
177        format!("{}.{}.{}", self.major, self.minor, self.patch)
178    }
179
180    /// 验证版本号格式的有效性
181    pub fn validate(&self) -> Result<()> {
182        // 通常版本号各部分都应该在合理范围内
183        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
203/// 版本号 serde 反序列化
204pub 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/// 版本比较结果
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub enum VersionComparison {
215    /// 版本相同
216    Equal,
217    /// 当前版本更新
218    Newer,
219    /// 可以应用补丁升级(同基础版本,更高build级别)
220    PatchUpgradeable,
221    /// 需要全量升级(不同基础版本)
222    FullUpgradeRequired,
223}
224
225impl Version {
226    /// 与另一个版本进行详细比较
227    ///
228    /// # 示例
229    /// ```
230    /// use client_core::version::{Version, VersionComparison};
231    /// use std::str::FromStr;
232    ///
233    /// let current = Version::from_str("0.0.13.2").unwrap();
234    /// let target = Version::from_str("0.0.13.5").unwrap();
235    ///
236    /// match current.compare_detailed(&target) {
237    ///     VersionComparison::PatchUpgradeable => println!("可以通过补丁升级"),
238    ///     VersionComparison::FullUpgradeRequired => println!("需要全量升级"),
239    ///     _ => {}
240    /// }
241    /// ```
242    pub fn compare_detailed(&self, server_version: &Version) -> VersionComparison {
243        if self == server_version {
244            return VersionComparison::Equal;
245        }
246
247        // 比较基础版本
248        if self.can_apply_patch(server_version) {
249            // 相同基础版本,比较build级别
250            if self.build < server_version.build {
251                VersionComparison::PatchUpgradeable
252            } else {
253                VersionComparison::Newer
254            }
255        } else {
256            // 不同基础版本
257            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        // 测试四段式版本号解析
275        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        // 测试三段式版本号解析(build默认为0)
282        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        // 测试无效格式
289        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        // 测试版本比较
302        assert!(v1 > v2);
303        assert!(v2 < v1);
304        assert!(v3 > v1);
305        assert!(v3 > v2);
306
307        // 测试相等
308        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        // 相同版本
349        let same = Version::from_str("0.0.13.2").unwrap();
350        assert_eq!(current.compare_detailed(&same), VersionComparison::Equal);
351
352        // 可以补丁升级
353        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        // 需要全量升级
360        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        // 当前版本更新
367        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)); // build级别更低
380        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    // Task 1.2 验收标准测试
393    #[test]
394    fn test_task_1_2_acceptance_criteria() {
395        // 验收标准:支持四段式版本号解析
396        let v1 = Version::from_str("0.0.13.5").expect("应该能解析四段式版本号");
397        let v2 = Version::from_str("0.0.13.2").expect("应该能解析四段式版本号");
398
399        // 验收标准:版本比较逻辑正常工作
400        assert!(v1 > v2, "版本比较应该正常工作");
401
402        // 验收标准:基础版本提取功能正常
403        assert_eq!(v1.base_version(), v2.base_version(), "基础版本应该相同");
404
405        // 验收标准:补丁适用性检查功能正常
406        assert!(v1.can_apply_patch(&v2), "补丁适用性检查应该正常工作");
407
408        println!("✅ Task 1.2: 版本管理系统重构 - 验收标准测试通过");
409        println!("   - ✅ 四段式版本号解析功能正常");
410        println!("   - ✅ 版本比较逻辑正常工作");
411        println!("   - ✅ 基础版本提取功能正常");
412        println!("   - ✅ 补丁适用性检查功能正常");
413        println!("   - ✅ 版本格式验证功能正常");
414    }
415}