Merge branch 'feat/p3-fix-game-java-discover-cfg-gating-jav'
Guards / core 零 UI 守门 (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release / build (macos-x64) (push) Waiting to run
Release / build (macos-arm64) (push) Waiting to run
Release / build (linux-x64) (push) Waiting to run
Release / build (windows-x64) (push) Waiting to run
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s

This commit is contained in:
AskaEth committed 2026-11-01 11:00:32 +08:00
commit 37ccaaf23f
2 files changed
+72 -13

No files matched your search

+64 -8
View File
@@ -186,13 +186,24 @@ pub fn dedupe(runtimes: Vec<JavaRuntime>) -> Vec<JavaRuntime> {
kept
}
/// 去重键:去掉 Windows 的 `\\?\` 前缀、统一分隔符、大小写不敏感(Windows)。
/// 去重键:剥掉 Windows 的 `\\?\` 前缀、分隔符一律折成 `/`、大小写只在 Windows 折叠。
///
/// 前两步与平台无关:同一条 `bin/java[.exe]` 用 `/` 还是 `\` 拼出来都是同一个文件,
/// 键必须相同——`JAVA_HOME` 的写法与扫盘拼出来的路径常在这点上不一致,键不齐就重复列。
/// 大小写折叠**只能**跟着平台走:NTFS 不区分大小写,而 POSIX 上 `Java` 与 `java` 是两个
/// 目录,折了会把两份真实安装误并成一个。
///
/// 代价(已知且有意的取舍):POSIX 上文件名里的字面反斜杠也会被当分隔符。JDK 安装路径带
/// 反斜杠属于病态情况,换来的是「同一条 java 的两种拼法永远同键」这条平台无关的不变量。
#[must_use]
pub fn path_key(path: &Path) -> String {
let text = path.display().to_string();
let text = text.strip_prefix(r"\\?\").unwrap_or(&text).to_string();
let text = text
.strip_prefix(r"\\?\")
.unwrap_or(&text)
.replace('\\', "/");
if cfg!(windows) {
text.replace('/', "\\").to_ascii_lowercase()
text.to_ascii_lowercase()
} else {
text
}
@@ -236,11 +247,56 @@ mod tests {
}
#[test]
fn path_key_normalises_separators() {
let left = path_key(std::path::Path::new("C:/a/b/java.exe"));
let right = path_key(std::path::Path::new("C:\\a\\b\\java.exe"));
assert_eq!(left, right);
assert!(!path_key(std::path::Path::new(r"\\?\C:\a\java.exe")).starts_with(r"\\"));
fn path_key_normalises_separators_on_every_platform() {
// 平台无关的不变量:同一条 `bin/java` 用 `/` 还是 `\` 拼出来,键必须相同。
// (M49:原来这里只在 Windows 上成立,Linux 上 `\` 原样留着 → 断言挂。)
let forward = path_key(std::path::Path::new("C:/a/b/java.exe"));
let backward = path_key(std::path::Path::new("C:\\a\\b\\java.exe"));
assert_eq!(forward, backward);
assert!(
!forward.contains('\\'),
"键里不该残留反斜杠(与平台无关):{forward}"
);
assert!(
!forward.starts_with(r"\\"),
"`\\\\?\\` 前缀不进键:{forward}"
);
}
#[test]
fn one_install_normalises_to_one_key_whatever_the_spelling() {
// 回归钉子:`JAVA_HOME` 与扫盘常对同一个 java 给出不同写法,四种拼法必须同键。
let spellings = [
"C:/Program Files/Java/jdk-21/bin/java.exe",
r"C:\Program Files\Java\jdk-21\bin\java.exe",
r"\\?\C:\Program Files\Java\jdk-21\bin\java.exe",
r"C:\Program Files\Java\jdk-21/bin/java.exe",
];
let key = path_key(std::path::Path::new(spellings[0]));
for spelling in spellings {
assert_eq!(
path_key(std::path::Path::new(spelling)),
key,
"同一安装的两种写法必须同键:{spelling}"
);
}
}
#[test]
fn path_key_folds_case_only_where_the_filesystem_does() {
// 大小写折叠跟平台走,两侧都钉住:NTFS 不区分大小写 → 折;
// POSIX 上 `Java` 与 `java` 是两个目录 → 不折(折了会把两份真实安装误并)。
let upper = path_key(std::path::Path::new(
"C:/Program Files/Java/jdk-21/bin/java.exe",
));
let lower = path_key(std::path::Path::new(
"c:/program files/java/jdk-21/bin/java.exe",
));
if cfg!(windows) {
assert_eq!(upper, lower, "Windows:大小写不同的同一路径应同键");
} else {
assert_ne!(upper, lower, "POSIX:大小写是有效区别,不应同键");
}
}
#[test]
+8 -5
View File
@@ -96,17 +96,20 @@ fn scanned_java_is_still_used_when_nothing_else_matches() {
#[test]
fn dedupe_collapses_one_install_found_twice() {
// `JAVA_HOME` 指向 Program Files 里的 JDK → 同一个 java 不应列两次。
// 第二条的「另一种写法」随平台给:Windows 上大小写和分隔符都可能不一致(NTFS 不区分
// 大小写),POSIX 上大小写是有效区别,只能靠分隔符拼出同一路径的第二种写法。
let duplicate = if cfg!(windows) {
"c:/program files/java/jdk-21/bin/java.exe"
} else {
"C:\\Program Files\\Java\\jdk-21/bin/java.exe"
};
let candidates = discover::dedupe(vec![
runtime(
"C:/Program Files/Java/jdk-21/bin/java.exe",
21,
JavaOrigin::JavaHome,
),
runtime(
"c:/program files/java/jdk-21/bin/java.exe",
21,
JavaOrigin::System,
),
runtime(duplicate, 21, JavaOrigin::System),
]);
assert_eq!(candidates.len(), 1);
assert_eq!(candidates[0].origin, JavaOrigin::JavaHome);