Skip to main content

archidoc_rust/
path_resolver.rs

1use std::path::Path;
2
3/// Convert a file path to dot-notation module path.
4///
5/// Examples:
6/// - `root/bus/mod.rs` relative to `root/` -> `bus`
7/// - `root/bus/calc/indicators/mod.rs` relative to `root/` -> `bus.calc.indicators`
8/// - `root/lib.rs` -> `_lib`
9/// - `root/foo.rs` -> `foo` (flat module at root)
10/// - `root/foo/bar.rs` -> `foo.bar` (flat module nested)
11pub fn path_to_module_name(path: &Path, root: &Path, filename: &str) -> String {
12    let relative = path.strip_prefix(root).unwrap_or(path);
13    let parent = relative.parent().unwrap_or(Path::new(""));
14
15    if filename == "lib.rs" {
16        return "_lib".to_string();
17    }
18
19    // Convert path components to dot notation
20    let parts: Vec<&str> = parent
21        .components()
22        .filter_map(|c| c.as_os_str().to_str())
23        .collect();
24
25    if filename == "mod.rs" {
26        // Traditional module structure: src/foo/mod.rs -> foo
27        parts.join(".")
28    } else {
29        // Flat module structure: src/foo.rs -> foo, src/foo/bar.rs -> foo.bar
30        let module_name = filename.strip_suffix(".rs").unwrap_or(filename);
31        if parts.is_empty() {
32            // Standalone file at root (e.g., router.rs)
33            module_name.to_string()
34        } else {
35            // Nested flat module: src/foo/bar.rs -> foo.bar
36            let mut full_parts = parts;
37            full_parts.push(module_name);
38            full_parts.join(".")
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use std::path::PathBuf;
47
48    #[test]
49    fn test_mod_rs_at_root() {
50        let root = PathBuf::from("/src");
51        let path = PathBuf::from("/src/bus/mod.rs");
52        assert_eq!(path_to_module_name(&path, &root, "mod.rs"), "bus");
53    }
54
55    #[test]
56    fn test_nested_mod_rs() {
57        let root = PathBuf::from("/src");
58        let path = PathBuf::from("/src/bus/calc/indicators/mod.rs");
59        assert_eq!(
60            path_to_module_name(&path, &root, "mod.rs"),
61            "bus.calc.indicators"
62        );
63    }
64
65    #[test]
66    fn test_lib_rs() {
67        let root = PathBuf::from("/src");
68        let path = PathBuf::from("/src/lib.rs");
69        assert_eq!(path_to_module_name(&path, &root, "lib.rs"), "_lib");
70    }
71
72    #[test]
73    fn test_flat_module_at_root() {
74        let root = PathBuf::from("/src");
75        let path = PathBuf::from("/src/router.rs");
76        assert_eq!(path_to_module_name(&path, &root, "router.rs"), "router");
77    }
78
79    #[test]
80    fn test_flat_module_nested() {
81        let root = PathBuf::from("/src");
82        let path = PathBuf::from("/src/bus/events.rs");
83        assert_eq!(
84            path_to_module_name(&path, &root, "events.rs"),
85            "bus.events"
86        );
87    }
88
89    #[test]
90    fn test_flat_module_deeply_nested() {
91        let root = PathBuf::from("/src");
92        let path = PathBuf::from("/src/bus/calc/indicators.rs");
93        assert_eq!(
94            path_to_module_name(&path, &root, "indicators.rs"),
95            "bus.calc.indicators"
96        );
97    }
98}