use std::path::{Path, PathBuf};
pub fn normalize_for_module_path(file_path: &Path, workspace_root: &Path) -> PathBuf {
if file_path.is_relative() {
workspace_root.join(file_path)
} else {
file_path.to_path_buf()
}
}
pub fn strip_source_root<'a>(path: &'a Path, source_roots: &[&str]) -> &'a Path {
for root in source_roots {
if let Ok(stripped) = path.strip_prefix(root) {
return stripped;
}
}
path
}
pub fn strip_source_root_owned(path: &Path, source_roots: &[&str]) -> PathBuf {
strip_source_root(path, source_roots).to_path_buf()
}
pub fn strip_extension<'a>(path_str: &'a str, extensions: &[&str]) -> &'a str {
for ext in extensions {
let suffix = format!(".{ext}");
if let Some(stripped) = path_str.strip_suffix(&suffix) {
return stripped;
}
}
path_str
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_normalize_relative_path() {
let file_path = Path::new("src/foo/bar.rs");
let temp_dir = TempDir::new().unwrap();
let workspace_root = temp_dir.path();
let result = normalize_for_module_path(file_path, workspace_root);
assert!(result.is_absolute());
assert!(result.ends_with("src/foo/bar.rs"));
}
#[test]
fn test_normalize_absolute_path() {
let temp_dir = TempDir::new().unwrap();
let workspace_root = temp_dir.path();
let file_path = workspace_root.join("src/foo/bar.rs");
let result = normalize_for_module_path(&file_path, workspace_root);
assert_eq!(result, file_path);
}
#[test]
fn test_strip_source_root_matches_first() {
let path = Path::new("src/foo/bar.rs");
let source_roots = &["src", "lib", "app"];
let result = strip_source_root(path, source_roots);
assert_eq!(result, Path::new("foo/bar.rs"));
}
#[test]
fn test_strip_source_root_matches_second() {
let path = Path::new("lib/utils/helper.rs");
let source_roots = &["src", "lib", "app"];
let result = strip_source_root(path, source_roots);
assert_eq!(result, Path::new("utils/helper.rs"));
}
#[test]
fn test_strip_source_root_no_match() {
let path = Path::new("tests/integration.rs");
let source_roots = &["src", "lib", "app"];
let result = strip_source_root(path, source_roots);
assert_eq!(result, path);
}
#[test]
fn test_strip_source_root_empty_roots() {
let path = Path::new("src/foo/bar.rs");
let source_roots: &[&str] = &[];
let result = strip_source_root(path, source_roots);
assert_eq!(result, path);
}
#[test]
fn test_strip_extension_simple() {
assert_eq!(strip_extension("foo.rs", &["rs"]), "foo");
assert_eq!(strip_extension("bar.py", &["py", "pyi"]), "bar");
}
#[test]
fn test_strip_extension_compound_typescript() {
let ts_extensions = &["d.ts", "tsx", "ts", "mts", "cts"];
assert_eq!(strip_extension("types.d.ts", ts_extensions), "types");
assert_eq!(strip_extension("component.tsx", ts_extensions), "component");
assert_eq!(strip_extension("main.ts", ts_extensions), "main");
}
#[test]
fn test_strip_extension_compound_php() {
let php_extensions = &["class.php", "inc.php", "php", "inc"];
assert_eq!(strip_extension("User.class.php", php_extensions), "User");
assert_eq!(strip_extension("config.inc.php", php_extensions), "config");
assert_eq!(strip_extension("index.php", php_extensions), "index");
}
#[test]
fn test_strip_extension_no_match() {
assert_eq!(strip_extension("README.md", &["rs", "py"]), "README.md");
assert_eq!(strip_extension("no_extension", &["rs"]), "no_extension");
}
#[test]
fn test_strip_extension_priority() {
let extensions = &["ts", "d.ts"]; assert_eq!(strip_extension("types.d.ts", extensions), "types.d");
let extensions = &["d.ts", "ts"];
assert_eq!(strip_extension("types.d.ts", extensions), "types");
}
}