优化: aichat效率剩余(压缩后台化防阻塞/审计批量事务/只读缓存轮内去重/流式增量渲染/AiCommandOutput合批/双渲染合并) + 跨端加固(df-project路径保留大小写/tunnel文档更正supervisor重连/relay固定时间比较与帧上限/启动校验) + 销账
This commit is contained in:
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user