Skip to main content

lean_ctx/core/
index_paths.rs

1//! Index path utilities — project-root normalization and graph match keys.
2//!
3//! Extracted from the deprecated `graph_index` module (#682) so these pure path
4//! helpers survive the removal of the in-memory `ProjectIndex` graph. They have
5//! no dependency on any graph backend — only `pathutil` and std.
6
7use 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}