lean_ctx/core/
index_paths.rs1use std::path::Path;
8
9pub(crate) fn normalize_absolute_path(path: &str) -> String {
10 if let Ok(canon) = crate::core::pathutil::safe_canonicalize(std::path::Path::new(path)) {
11 return canon.to_string_lossy().to_string();
12 }
13
14 let mut normalized = path.to_string();
15 while normalized.ends_with("\\.") || normalized.ends_with("/.") {
16 normalized.truncate(normalized.len() - 2);
17 }
18 while normalized.len() > 1
19 && (normalized.ends_with('\\') || normalized.ends_with('/'))
20 && !normalized.ends_with(":\\")
21 && !normalized.ends_with(":/")
22 && normalized != "\\"
23 && normalized != "/"
24 {
25 normalized.pop();
26 }
27 normalized
28}
29
30pub fn normalize_project_root(path: &str) -> String {
31 normalize_absolute_path(path)
32}
33
34pub fn graph_match_key(path: &str) -> String {
35 let stripped =
36 crate::core::pathutil::strip_verbatim_str(path).unwrap_or_else(|| path.replace('\\', "/"));
37 stripped.trim_start_matches('/').to_string()
38}
39
40pub fn graph_relative_key(path: &str, root: &str) -> String {
41 let root_norm = normalize_project_root(root);
42 let path_norm = normalize_absolute_path(path);
43 let root_path = Path::new(&root_norm);
44 let path_path = Path::new(&path_norm);
45
46 if let Ok(rel) = path_path.strip_prefix(root_path) {
47 let rel = rel.to_string_lossy().to_string();
48 return rel.trim_start_matches(['/', '\\']).to_string();
49 }
50
51 path.trim_start_matches(['/', '\\'])
52 .replace('/', std::path::MAIN_SEPARATOR_STR)
53}