Skip to main content

argyph_graph/resolve/
mod.rs

1use camino::Utf8Path;
2
3pub mod python;
4pub mod rust;
5pub mod typescript;
6
7pub trait ImportResolver {
8    fn resolve_import(
9        &self,
10        source_file: &Utf8Path,
11        module_path: &[String],
12        raw: &str,
13    ) -> Option<ModuleTarget>;
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ModuleTarget {
18    pub file_path: String,
19    pub confidence: super::edge::Confidence,
20}
21
22pub(crate) fn normalize_path(path: &str) -> String {
23    // Resolvers build candidate paths via `Utf8PathBuf::push`, which uses the
24    // platform separator (`\` on Windows). Normalize to `/` so module paths
25    // are stable across platforms and match the `/`-normalized graph nodes.
26    let path = path.replace('\\', "/");
27    let is_absolute = path.starts_with('/');
28    let parts: Vec<&str> = path.split('/').filter(|p| !p.is_empty()).collect();
29    let mut out: Vec<&str> = Vec::new();
30    for part in parts {
31        if part == "." {
32            continue;
33        }
34        if part == ".." {
35            out.pop();
36        } else {
37            out.push(part);
38        }
39    }
40    let result = out.join("/");
41    if is_absolute {
42        format!("/{result}")
43    } else {
44        result
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn normalizes_dotdot() {
54        assert_eq!(normalize_path("a/b/../c"), "a/c");
55    }
56
57    #[test]
58    fn strips_dots() {
59        assert_eq!(normalize_path("a/./b"), "a/b");
60    }
61
62    #[test]
63    fn handles_multiple_dotdots() {
64        assert_eq!(normalize_path("a/b/../../c"), "c");
65    }
66
67    #[test]
68    fn preserves_absolute_path() {
69        assert_eq!(normalize_path("/a/b/../c"), "/a/c");
70    }
71}