fix(adapters): 按数值比较 Unity 版本范围

VersionRange::contains 原为字符串字典序比较,2021.3.9f1 等带发布后缀的
正式版本会被 can_handle/parse 误拒,多位补丁号排序也不正确。改为解析
(major, minor, patch) 数值元组比较,补丁号剥离发布后缀,无法解析的版本
一律视为不在范围内,并补充多位补丁号、后缀版本和畸形版本的测试。

对应 issue #18 维护清单 1-1。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 10:27:28 -07:00
co-authored by Claude Fable 5
parent 060b7d73e5
commit 7c2f6824a6
2 changed files with 66 additions and 1 deletions
+65 -1
View File
@@ -21,10 +21,40 @@ impl VersionRange {
} }
/// 检查版本是否在范围内 /// 检查版本是否在范围内
///
/// 按 `(major, minor, patch)` 数值比较,补丁号允许携带 Unity 发布后缀
/// (例如 `2021.3.9f1` 按补丁号 9 参与比较);无法解析的版本一律视为不在范围内。
pub fn contains(&self, version: &str) -> bool { pub fn contains(&self, version: &str) -> bool {
version >= self.min.as_str() && version <= self.max.as_str() match (
parse_version_components(version),
parse_version_components(&self.min),
parse_version_components(&self.max),
) {
(Some(version), Some(min), Some(max)) => version >= min && version <= max,
_ => false,
} }
} }
}
/// 将 Unity 版本字符串解析为 `(major, minor, patch)` 数值元组。
///
/// `major`/`minor` 必须是纯数字;`patch` 取第三段的前导数字,
/// 以兼容 `2021.3.9f1` 这类带发布后缀的正式版本号。
fn parse_version_components(version: &str) -> Option<(u64, u64, u64)> {
let mut parts = version.split('.');
let major = parts.next()?.parse::<u64>().ok()?;
let minor = parts.next()?.parse::<u64>().ok()?;
let patch_part = parts.next()?;
let digit_count = patch_part
.chars()
.take_while(|character| character.is_ascii_digit())
.count();
if digit_count == 0 {
return None;
}
let patch = patch_part[..digit_count].parse::<u64>().ok()?;
Some((major, minor, patch))
}
/// 原始 AssetBundle 数据 /// 原始 AssetBundle 数据
#[derive(Debug)] #[derive(Debug)]
@@ -175,6 +205,40 @@ mod tests {
assert!(!range.contains("2021.4.0")); assert!(!range.contains("2021.4.0"));
} }
#[test]
fn test_version_range_compares_patch_numerically() {
let range = VersionRange::new("2021.3.0", "2021.3.99");
assert!(!range.contains("2021.3.100"));
assert!(range.contains("2021.3.9"));
let wide_range = VersionRange::new("2021.3.20", "2021.3.120");
assert!(wide_range.contains("2021.3.100"));
assert!(!wide_range.contains("2021.3.9"));
}
#[test]
fn test_version_range_accepts_release_suffix() {
let range = VersionRange::new("2021.3.0", "2021.3.99");
assert!(range.contains("2021.3.9f1"));
assert!(range.contains("2021.3.56f2"));
assert!(range.contains("2021.3.99f1"));
assert!(!range.contains("2021.3.100f1"));
}
#[test]
fn test_version_range_rejects_malformed_versions() {
let range = VersionRange::new("2021.3.0", "2021.3.99");
assert!(!range.contains(""));
assert!(!range.contains("unknown"));
assert!(!range.contains("2021"));
assert!(!range.contains("2021.3"));
assert!(!range.contains("2021.x.5"));
assert!(!range.contains("2021.3.f1"));
}
#[test] #[test]
fn test_raw_assetbundle() { fn test_raw_assetbundle() {
let bundle = RawAssetBundle { let bundle = RawAssetBundle {
+1
View File
@@ -436,6 +436,7 @@ mod tests {
assert!(range.contains("2021.3.0")); assert!(range.contains("2021.3.0"));
assert!(range.contains("2021.3.56")); assert!(range.contains("2021.3.56"));
assert!(range.contains("2021.3.9f1"));
assert!(!range.contains("2021.2.0")); assert!(!range.contains("2021.2.0"));
} }