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///
49/// let v1 = Version::from_str("0.0.13.5").unwrap();
50/// assert_eq!(v1.major, 0);
51/// assert_eq!(v1.minor, 0);
52/// assert_eq!(v1.patch, 13);
53/// assert_eq!(v1.build, 5);
54/// ```
55impl FromStr for Version {
56    type Err = anyhow::Error;
57
58    fn from_str(s: &str) -> Result<Self> {
59        if s.is_empty() {
60            return Err(anyhow::anyhow!("Version string cannot be empty"));
61        }
62        let version =
63            Version::parse_version(s).map_err(|e| anyhow::anyhow!("Failed to parse version: {}", e))?;
64        Ok(version)
65    }
66}
67
68impl Version {
69    /// 创建新的版本号
70    pub fn new(major: u32, minor: u32, patch: u32, build: Option<u32>) -> Self {
71        match build {
72            Some(build) => Self {
73                major,
74                minor,
75                patch,
76                build,
77            },
78            None => Self::new_without_build(major, minor, patch),
79        }
80    }
81    pub fn new_without_build(major: u32, minor: u32, patch: u32) -> Self {
82        Self {
83            major,
84            minor,
85            patch,
86            ..Default::default()
87        }
88    }
89
90    /// 解析版本号,比如"v0.0.13.5","v0.1.2"
91    fn parse_version(input: &str) -> ModalResult<Version> {
92        let mut input_slice = input;
93
94        let (_, major, minor, patch, build) = seq!(
95            opt(alt(("v", "V"))),
96            digit1.parse_to::<u32>(),
97            preceded('.', digit1.parse_to::<u32>()),
98            preceded('.', digit1.parse_to::<u32>()),
99            opt(preceded('.', digit1.parse_to::<u32>())),
100        )
101        .parse_next(&mut input_slice)?;
102
103        // 检查是否完全消耗输入
104        if !input_slice.is_empty() {
105            // 创建一个简单的错误,包含额外的字符信息
106            let error_msg = format!(
107                "Invalid version format: extra characters '{}' found after '{}'",
108                input_slice,
109                &input[..input.len() - input_slice.len()]
110            );
111            error!("{}", error_msg);
112            return Err(ErrMode::Cut(ContextError::default()));
113        }
114
115        Ok(Version::new(major, minor, patch, build))
116    }
117
118    /// 获取基础版本(不包含build级别)
119    ///
120    /// # 示例
121    /// ```
122    /// use client_core::version::Version;
123    ///
124    /// let v = Version::from_str("0.0.13.5").unwrap();
125    /// let base = v.base_version();
126    /// assert_eq!(base.to_string(), "0.0.13.0");
127    /// ```
128    pub fn base_version(&self) -> Version {
129        Version::new_without_build(self.major, self.minor, self.patch)
130    }
131
132    /// 检查是否可以在当前版本上应用指定的补丁
133    ///
134    /// 补丁只能应用在相同的基础版本上
135    ///
136    /// # 示例
137    /// ```
138    /// use client_core::version::Version;
139    ///
140    /// let current = Version::from_str("0.0.13.2").unwrap();
141    /// let patch_target = Version::from_str("0.0.13.0").unwrap();
142    /// assert!(current.can_apply_patch(&patch_target));
143    ///
144    /// let different_base = Version::from_str("0.0.14.0").unwrap();
145    /// assert!(!current.can_apply_patch(&different_base));
146    /// ```
147    pub fn can_apply_patch(&self, patch_base_version: &Version) -> bool {
148        self.base_version() == patch_base_version.base_version()
149    }
150
151    /// 检查当前版本是否兼容指定的补丁版本
152    ///
153    /// 当前版本的基础版本必须与补丁版本的基础版本相同,
154    /// 且当前的build级别不能超过补丁版本
155    pub fn is_compatible_with_patch(&self, patch_version: &Version) -> bool {
156        self.base_version() == patch_version.base_version() && self.build <= patch_version.build
157    }
158
159    /// 获取版本字符串的简短表示(不包含build为0的情况)
160    ///
161    /// # 示例
162    /// - Version(0, 0, 13, 0) -> "0.0.13"
163    /// - Version(0, 0, 13, 5) -> "0.0.13.5"
164    pub fn to_short_string(&self) -> String {
165        if self.build == 0 {
166            format!("{}.{}.{}", self.major, self.minor, self.patch)
167        } else {
168            self.to_string()
169        }
170    }
171
172    /// 获取基础版本字符串
173    pub fn base_version_string(&self) -> String {
174        format!("{}.{}.{}", self.major, self.minor, self.patch)
175    }
176
177    /// 验证版本号格式的有效性
178    pub fn validate(&self) -> Result<()> {
179        // 通常版本号各部分都应该在合理范围内
180        if self.major > 999 || self.minor > 999 || self.patch > 999 || self.build > 9999 {
181            return Err(anyhow::anyhow!(
182                "Version number values are too large and may be invalid"
183            ));
184        }
185
186        Ok(())
187    }
188}
189
190impl Display for Version {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        write!(
193            f,
194            "{}.{}.{}.{}",
195            self.major, self.minor, self.patch, self.build
196        )
197    }
198}
199
200/// 版本号 serde 反序列化
201pub fn version_from_str<'de, D>(deserializer: D) -> std::result::Result<Version, D::Error>
202where
203    D: Deserializer<'de>,
204{
205    let s = String::deserialize(deserializer)?;
206    Version::from_str(&s).map_err(serde::de::Error::custom)
207}
208
209/// 版本比较结果
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub enum VersionComparison {
212    /// 版本相同
213    Equal,
214    /// 当前版本更新
215    Newer,
216    /// 可以应用补丁升级(同基础版本,更高build级别)
217    PatchUpgradeable,
218    /// 需要全量升级(不同基础版本)
219    FullUpgradeRequired,
220}
221
222impl Version {
223    /// 与另一个版本进行详细比较
224    ///
225    /// # 示例
226    /// ```
227    /// use client_core::version::{Version, VersionComparison};
228    ///
229    /// let current = Version::from_str("0.0.13.2").unwrap();
230    /// let target = Version::from_str("0.0.13.5").unwrap();
231    ///
232    /// match current.compare_detailed(&target) {
233    ///     VersionComparison::PatchUpgradeable => println!("可以通过补丁升级"),
234    ///     VersionComparison::FullUpgradeRequired => println!("需要全量升级"),
235    ///     _ => {}
236    /// }
237    /// ```
238    pub fn compare_detailed(&self, server_version: &Version) -> VersionComparison {
239        if self == server_version {
240            return VersionComparison::Equal;
241        }
242
243        // 比较基础版本
244        if self.can_apply_patch(server_version) {
245            // 相同基础版本,比较build级别
246            if self.build < server_version.build {
247                VersionComparison::PatchUpgradeable
248            } else {
249                VersionComparison::Newer
250            }
251        } else {
252            // 不同基础版本
253            let self_base = self.base_version();
254            let server_base = server_version.base_version();
255            if self_base < server_base {
256                VersionComparison::FullUpgradeRequired
257            } else {
258                VersionComparison::Newer
259            }
260        }
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267
268    #[test]
269    fn test_version_parsing() {
270        // 测试四段式版本号解析
271        let v1 = Version::from_str("0.0.13.5").unwrap();
272        assert_eq!(v1.major, 0);
273        assert_eq!(v1.minor, 0);
274        assert_eq!(v1.patch, 13);
275        assert_eq!(v1.build, 5);
276
277        // 测试三段式版本号解析(build默认为0)
278        let v2 = Version::from_str("1.2.3").unwrap();
279        assert_eq!(v2.major, 1);
280        assert_eq!(v2.minor, 2);
281        assert_eq!(v2.patch, 3);
282        assert_eq!(v2.build, 0);
283
284        // 测试无效格式
285        assert!(Version::from_str("1.2").is_err());
286        assert!(Version::from_str("1.2.3.4.5").is_err());
287        assert!(Version::from_str("").is_err());
288        assert!(Version::from_str("a.b.c").is_err());
289    }
290
291    #[test]
292    fn test_version_comparison() {
293        let v1 = Version::from_str("0.0.13.5").unwrap();
294        let v2 = Version::from_str("0.0.13.2").unwrap();
295        let v3 = Version::from_str("0.0.14.0").unwrap();
296
297        // 测试版本比较
298        assert!(v1 > v2);
299        assert!(v2 < v1);
300        assert!(v3 > v1);
301        assert!(v3 > v2);
302
303        // 测试相等
304        let v4 = Version::from_str("0.0.13.5").unwrap();
305        assert_eq!(v1, v4);
306    }
307
308    #[test]
309    fn test_base_version() {
310        let v1 = Version::from_str("0.0.13.5").unwrap();
311        let base = v1.base_version();
312
313        assert_eq!(base.major, 0);
314        assert_eq!(base.minor, 0);
315        assert_eq!(base.patch, 13);
316        assert_eq!(base.build, 0);
317        assert_eq!(base.to_string(), "0.0.13.0");
318    }
319
320    #[test]
321    fn test_can_apply_patch() {
322        let current = Version::from_str("0.0.13.2").unwrap();
323        let patch_target = Version::from_str("0.0.13.0").unwrap();
324        let different_base = Version::from_str("0.0.14.0").unwrap();
325
326        assert!(current.can_apply_patch(&patch_target));
327        assert!(!current.can_apply_patch(&different_base));
328    }
329
330    #[test]
331    fn test_version_display() {
332        let v = Version::from_str("0.0.13.5").unwrap();
333        assert_eq!(v.to_string(), "0.0.13.5");
334
335        let v_short = Version::from_str("0.0.13.0").unwrap();
336        assert_eq!(v_short.to_short_string(), "0.0.13");
337        assert_eq!(v.to_short_string(), "0.0.13.5");
338    }
339
340    #[test]
341    fn test_detailed_comparison() {
342        let current = Version::from_str("0.0.13.2").unwrap();
343
344        // 相同版本
345        let same = Version::from_str("0.0.13.2").unwrap();
346        assert_eq!(current.compare_detailed(&same), VersionComparison::Equal);
347
348        // 可以补丁升级
349        let patch_upgrade = Version::from_str("0.0.13.5").unwrap();
350        assert_eq!(
351            current.compare_detailed(&patch_upgrade),
352            VersionComparison::PatchUpgradeable
353        );
354
355        // 需要全量升级
356        let full_upgrade = Version::from_str("0.0.14.0").unwrap();
357        assert_eq!(
358            current.compare_detailed(&full_upgrade),
359            VersionComparison::FullUpgradeRequired
360        );
361
362        // 当前版本更新
363        let older = Version::from_str("0.0.12.0").unwrap();
364        assert_eq!(current.compare_detailed(&older), VersionComparison::Newer);
365    }
366
367    #[test]
368    fn test_compatibility() {
369        let current = Version::from_str("0.0.13.2").unwrap();
370        let patch_v1 = Version::from_str("0.0.13.5").unwrap();
371        let patch_v2 = Version::from_str("0.0.13.1").unwrap();
372        let different_base = Version::from_str("0.0.14.0").unwrap();
373
374        assert!(current.is_compatible_with_patch(&patch_v1));
375        assert!(!current.is_compatible_with_patch(&patch_v2)); // build级别更低
376        assert!(!current.is_compatible_with_patch(&different_base));
377    }
378
379    #[test]
380    fn test_validation() {
381        let valid_v = Version::from_str("0.0.13.5").unwrap();
382        assert!(valid_v.validate().is_ok());
383
384        let invalid_v = Version::new(1000, 1000, 1000, Some(10000));
385        assert!(invalid_v.validate().is_err());
386    }
387
388    // Task 1.2 验收标准测试
389    #[test]
390    fn test_task_1_2_acceptance_criteria() {
391        // 验收标准:支持四段式版本号解析
392        let v1 = Version::from_str("0.0.13.5").expect("应该能解析四段式版本号");
393        let v2 = Version::from_str("0.0.13.2").expect("应该能解析四段式版本号");
394
395        // 验收标准:版本比较逻辑正常工作
396        assert!(v1 > v2, "版本比较应该正常工作");
397
398        // 验收标准:基础版本提取功能正常
399        assert_eq!(v1.base_version(), v2.base_version(), "基础版本应该相同");
400
401        // 验收标准:补丁适用性检查功能正常
402        assert!(v1.can_apply_patch(&v2), "补丁适用性检查应该正常工作");
403
404        println!("✅ Task 1.2: 版本管理系统重构 - 验收标准测试通过");
405        println!("   - ✅ 四段式版本号解析功能正常");
406        println!("   - ✅ 版本比较逻辑正常工作");
407        println!("   - ✅ 基础版本提取功能正常");
408        println!("   - ✅ 补丁适用性检查功能正常");
409        println!("   - ✅ 版本格式验证功能正常");
410    }
411}