mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-06-29 01:45:54 +08:00
fix: 全面审查修复 14 个 bug,新增 Rust 单元测试
CRITICAL: - PathTable/MergePreview 操作后不重新渲染(加 dataVersion 版本号机制) - moveUp/moveDown 后 selectedIndices 过时(更新到新位置) HIGH: - ImportDialog 显示 "true" 而非路径数量(改为 number 类型) - F1 快捷键无效果(添加 onHelp 回调) - useKeyboard 每次渲染重复注册事件(改用 ref 模式) - batch delete 撤销顺序错误(拆分为独立记录) - importPaths 存储数组引用而非副本 - StringList.all 暴露内部数组(改为返回副本) - expand_env_vars 静默吞 API 错误(加 log::warn) - join_path 写入前未修剪路径(加 trim 避免注册表污染) MEDIUM: - handleClean 总传 () => true 不验证无效路径 - HelpDialog/ImportDialog 缺 Escape 关闭 - initDarkMode 不同步 Zustand store - 多处硬编码中文改为 i18n.t() - Rust unsafe 块补全 SAFETY 注释 新增 Rust 测试: system.rs 4 个单元测试 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -77,7 +77,12 @@ fn split_path(raw: &str) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 用分号连接路径列表
|
||||
/// 用分号连接路径列表(去除首尾空格避免污染注册表)
|
||||
fn join_path(paths: &[String]) -> String {
|
||||
paths.join(";")
|
||||
paths
|
||||
.iter()
|
||||
.map(|p| p.trim())
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(";")
|
||||
}
|
||||
|
||||
@@ -29,28 +29,32 @@ pub fn expand_env_vars(path: &str) -> String {
|
||||
return path.to_string();
|
||||
}
|
||||
|
||||
// 转为 UTF-16 宽字符串
|
||||
// 转为 UTF-16 宽字符串(以 null 结尾)
|
||||
let wide_path: Vec<u16> = path
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
// 先查询需要的缓冲区大小 (lpDst=NULL)
|
||||
// SAFETY: wide_path 是以 null 结尾的 UTF-16 字符串,lpDst 为 null 且 nSize 为 0,
|
||||
// 根据 MSDN 文档此时 API 只查询所需缓冲区大小而不写入数据
|
||||
let required = unsafe {
|
||||
ExpandEnvironmentStringsW(wide_path.as_ptr(), std::ptr::null_mut(), 0)
|
||||
};
|
||||
|
||||
if required == 0 {
|
||||
log::warn!("expand_env_vars: API 查询缓冲区失败, 返回原始路径: {path}");
|
||||
return path.to_string();
|
||||
}
|
||||
|
||||
// 实际展开
|
||||
// SAFETY: buffer 容量为 required(API 返回的精确大小),wide_path 以 null 结尾,
|
||||
// 且两个指针指向不同的内存区域,不存在重叠
|
||||
let mut buffer: Vec<u16> = vec![0; required as usize];
|
||||
let result = unsafe {
|
||||
ExpandEnvironmentStringsW(wide_path.as_ptr(), buffer.as_mut_ptr(), required)
|
||||
};
|
||||
|
||||
if result == 0 {
|
||||
log::warn!("expand_env_vars: 展开失败, 返回原始路径: {path}");
|
||||
return path.to_string();
|
||||
}
|
||||
|
||||
@@ -66,8 +70,11 @@ pub fn broadcast_env_change() {
|
||||
const WM_SETTINGCHANGE: u32 = 0x001A;
|
||||
const SMTO_ABORTIFHUNG: u32 = 0x0002;
|
||||
|
||||
// SAFETY: env_str 是以 null 结尾的 UTF-16 字符串,所有指针和常量均遵循 Win32 API 约定
|
||||
let env_str: Vec<u16> = "Environment\0".encode_utf16().collect();
|
||||
|
||||
// SAFETY: env_str.as_ptr() 指向以 null 结尾的字符串,HWND_BROADCAST 是合法句柄,
|
||||
// lpdwResult 为 null 表示不需要返回值,其他参数均为常量
|
||||
let result = unsafe {
|
||||
SendMessageTimeoutW(
|
||||
HWND_BROADCAST,
|
||||
@@ -108,3 +115,34 @@ extern "system" {
|
||||
lpdwResult: *mut usize,
|
||||
) -> isize;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn validate_path_env_var_always_valid() {
|
||||
assert!(validate_path("%JAVA_HOME%\\bin"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_env_vars_no_percent_returns_original() {
|
||||
let result = expand_env_vars("C:\\Windows");
|
||||
assert_eq!(result, "C:\\Windows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_env_vars_with_invalid_var_returns_original() {
|
||||
// 展开不存在的变量可能会回归原始值或产生部分展开;测试是否不会崩溃
|
||||
let result = expand_env_vars("%__NONEXISTENT_VAR__%");
|
||||
// 至少不应为空白
|
||||
assert!(!result.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_admin_returns_bool() {
|
||||
let result = check_admin();
|
||||
// 在任意机器上应返回 true 或 false,不应 panic
|
||||
assert!((result == true) || (result == false));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user