use alloc::vec::Vec;
use crate::Language;
pub fn module_segments(language: Language, path: &str) -> Vec<&str> {
let (directories, file) = path.rsplit_once('/').unwrap_or(("", path));
let stem = file_stem(file);
let module = (package_file(language) != Some(stem)).then_some(stem);
directories
.split('/')
.chain(module)
.flat_map(|part| part.split('.'))
.filter(|segment| !segment.is_empty())
.collect()
}
fn file_stem(file: &str) -> &str {
match file.rsplit_once('.') {
Some((stem, _)) if !stem.is_empty() => stem,
_ => file,
}
}
const fn package_file(language: Language) -> Option<&'static str> {
match language {
Language::Python => Some("__init__"),
Language::JavaScript | Language::TypeScript => Some("index"),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::module_segments;
use crate::Language;
#[test]
fn a_python_file_is_a_module_inside_its_directories() {
assert_eq!(
module_segments(Language::Python, "pkg/mod.py"),
["pkg", "mod"]
);
}
#[test]
fn a_python_package_file_is_the_module_of_its_directory() {
assert_eq!(
module_segments(Language::Python, "pkg/__init__.py"),
["pkg"]
);
}
#[test]
fn a_package_file_at_the_root_names_no_module() {
assert!(module_segments(Language::Python, "__init__.py").is_empty());
}
#[test]
fn a_python_file_named_index_is_an_ordinary_module() {
assert_eq!(
module_segments(Language::Python, "pkg/index.py"),
["pkg", "index"]
);
}
#[test]
fn a_javascript_index_file_is_the_module_of_its_directory() {
assert_eq!(
module_segments(Language::JavaScript, "lib/index.js"),
["lib"]
);
}
#[test]
fn each_dot_of_a_file_name_separates_a_segment_as_in_a_dotted_import() {
assert_eq!(
module_segments(Language::JavaScript, "src/a.test.js"),
["src", "a", "test"]
);
}
#[test]
fn a_language_without_package_files_keeps_every_file_name() {
assert_eq!(
module_segments(Language::Go, "cmd/main.go"),
["cmd", "main"]
);
}
}