优化: aichat效率剩余(压缩后台化防阻塞/审计批量事务/只读缓存轮内去重/流式增量渲染/AiCommandOutput合批/双渲染合并) + 跨端加固(df-project路径保留大小写/tunnel文档更正supervisor重连/relay固定时间比较与帧上限/启动校验) + 销账

This commit is contained in:
lxy
2026-08-09 21:35:59 +08:00
parent fbd8fae44b
commit 11f4978ec1
15 changed files with 841 additions and 193 deletions
+45 -3
View File
@@ -61,10 +61,10 @@ pub(super) fn truncate_chars(s: &str, max: usize) -> String {
format!("{}…(已截断)", truncated)
}
/// 规范化路径用于比较:canonicalize 解析绝对规范路径(失败降级),
/// 规范化路径用于比较/查重:canonicalize 解析绝对规范路径(失败降级),
/// 统一正斜杠 + 小写。防 `C:\a\b` vs `C:/a/b/` 绕过重复检查。
/// 注:仅用于比较,存库保留用户输入的原始可读路径
pub fn normalize_path(p: &str) -> String {
/// 注:仅用于比较,存库请用 [`canonicalize_for_store`](保留大小写)
pub fn normalize_for_compare(p: &str) -> String {
match Path::new(p).canonicalize() {
Ok(abs) => abs.to_string_lossy().replace('\\', "/").to_lowercase(),
Err(_) => p
@@ -74,6 +74,22 @@ pub fn normalize_path(p: &str) -> String {
}
}
/// 规范化路径用于存库:canonicalize 解析绝对规范路径(失败降级为 trim + 正斜杠),
/// **保留大小写**(大小写敏感系统如 Linux/容器文件系统路径解析需原样大小写,
/// 小写化会导致路径解析失败)。比较/查重请用 [`normalize_for_compare`]。
pub fn canonicalize_for_store(p: &str) -> String {
match Path::new(p).canonicalize() {
Ok(abs) => abs.to_string_lossy().replace('\\', "/"),
Err(_) => p.trim_end_matches(['\\', '/']).replace('\\', "/"),
}
}
/// 兼容别名:小写比较版(等价 [`normalize_for_compare`])。
/// 旧调用方沿用;新代码按用途选 `normalize_for_compare`(比较)/ `canonicalize_for_store`(存库)。
pub fn normalize_path(p: &str) -> String {
normalize_for_compare(p)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -111,4 +127,30 @@ mod tests {
assert!(!n.contains('\\'), "反斜杠未归一: {n}");
assert_eq!(n, n.to_lowercase(), "未小写: {n}");
}
#[test]
fn canonicalize_for_store_preserves_case() {
// 存库版保留大小写(降级分支:不存在的路径走 trim + replace,大小写原样保留)
let n = canonicalize_for_store(r"C:\Foo\Bar\");
assert!(!n.contains('\\'), "反斜杠未归一: {n}");
assert!(
!n.ends_with('/') && !n.ends_with('\\'),
"尾部分隔符未裁剪: {n}"
);
assert!(
n.contains("Foo") && n.contains("Bar"),
"存库版不应小写化(大小写敏感系统路径解析依赖): {n}"
);
}
#[test]
fn compare_lowercases_but_store_keeps_case() {
// compare 版与 store 版语义分离:比较小写、存库保留大小写
let p = r"C:\Foo\Bar";
let comp = normalize_for_compare(p);
let store = canonicalize_for_store(p);
assert_eq!(comp, comp.to_lowercase(), "比较版应小写: {comp}");
assert_ne!(comp, store, "小写化仅限比较版,存库版应保留大小写: {store}");
assert!(!store.contains('\\'), "存库版应正斜杠: {store}");
}
}