agent_chain_core/api/
path.rs

1//! Path utilities for converting file paths to import paths.
2
3use std::path::{Path, PathBuf};
4
5/// Get the path of the file as a relative path to a base directory.
6///
7/// # Arguments
8///
9/// * `file` - The file path to convert.
10/// * `relative_to` - The base path to make the file path relative to.
11///
12/// # Returns
13///
14/// The relative path as a `PathBuf`, or `None` if the path cannot be made relative.
15///
16/// # Example
17///
18/// ```
19/// use std::path::Path;
20/// use agent_chain_core::api::get_relative_path;
21///
22/// let base = Path::new("/home/user/project");
23/// let file = Path::new("/home/user/project/src/main.rs");
24/// let relative = get_relative_path(file, base);
25/// assert_eq!(relative, Some("src/main.rs".into()));
26/// ```
27pub fn get_relative_path<P: AsRef<Path>, B: AsRef<Path>>(
28    file: P,
29    relative_to: B,
30) -> Option<String> {
31    let file = file.as_ref();
32    let base = relative_to.as_ref();
33
34    file.strip_prefix(base)
35        .ok()
36        .map(|p| p.to_string_lossy().into_owned())
37}
38
39/// Convert a file path to an import path (module path format).
40///
41/// This converts a file path like `src/api/path.rs` to a module path like `api::path`.
42///
43/// # Arguments
44///
45/// * `file` - The file path to convert.
46/// * `suffix` - An optional suffix to append to the import path.
47/// * `relative_to` - The base path to make the file path relative to.
48///
49/// # Returns
50///
51/// The import path as a `String`, or `None` if the conversion fails.
52///
53/// # Example
54///
55/// ```
56/// use std::path::Path;
57/// use agent_chain_core::api::as_import_path;
58///
59/// let base = Path::new("/home/user/project/src");
60/// let file = Path::new("/home/user/project/src/api/path.rs");
61/// let import = as_import_path(file, None, base);
62/// assert_eq!(import, Some("api::path".to_string()));
63/// ```
64pub fn as_import_path<P: AsRef<Path>, B: AsRef<Path>>(
65    file: P,
66    suffix: Option<&str>,
67    relative_to: B,
68) -> Option<String> {
69    let file = file.as_ref();
70    let relative_path = get_relative_path(file, relative_to)?;
71
72    let path = PathBuf::from(&relative_path);
73
74    // Remove file extension if it's a file
75    let without_extension = if path.extension().is_some() {
76        path.with_extension("")
77    } else {
78        path
79    };
80
81    // Convert path separators to `::`
82    let import_path = without_extension
83        .to_string_lossy()
84        .replace([std::path::MAIN_SEPARATOR, '/', '\\'], "::");
85
86    if let Some(suffix) = suffix {
87        Some(format!("{}::{}", import_path, suffix))
88    } else {
89        Some(import_path)
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use std::path::Path;
97
98    #[test]
99    fn test_get_relative_path() {
100        let base = Path::new("/home/user/project");
101        let file = Path::new("/home/user/project/src/main.rs");
102        let relative = get_relative_path(file, base);
103        assert_eq!(relative, Some("src/main.rs".to_string()));
104    }
105
106    #[test]
107    fn test_get_relative_path_same_dir() {
108        let base = Path::new("/home/user/project");
109        let file = Path::new("/home/user/project/main.rs");
110        let relative = get_relative_path(file, base);
111        assert_eq!(relative, Some("main.rs".to_string()));
112    }
113
114    #[test]
115    fn test_get_relative_path_not_relative() {
116        let base = Path::new("/home/user/project");
117        let file = Path::new("/other/path/main.rs");
118        let relative = get_relative_path(file, base);
119        assert_eq!(relative, None);
120    }
121
122    #[test]
123    fn test_as_import_path() {
124        let base = Path::new("/home/user/project/src");
125        let file = Path::new("/home/user/project/src/api/path.rs");
126        let import = as_import_path(file, None, base);
127        assert_eq!(import, Some("api::path".to_string()));
128    }
129
130    #[test]
131    fn test_as_import_path_with_suffix() {
132        let base = Path::new("/home/user/project/src");
133        let file = Path::new("/home/user/project/src/api/path.rs");
134        let import = as_import_path(file, Some("MyStruct"), base);
135        assert_eq!(import, Some("api::path::MyStruct".to_string()));
136    }
137
138    #[test]
139    fn test_as_import_path_directory() {
140        let base = Path::new("/home/user/project/src");
141        let file = Path::new("/home/user/project/src/api");
142        let import = as_import_path(file, None, base);
143        assert_eq!(import, Some("api".to_string()));
144    }
145}