Skip to main content

perl_module/path/
mod.rs

1//! Perl module name/path conversion helpers.
2//!
3//! Provides a small, focused API for converting between canonical Perl module
4//! names (e.g., `Foo::Bar`) and module file paths (e.g., `Foo/Bar.pm`).
5
6use std::borrow::Cow;
7
8/// Normalize legacy package separator `'` to canonical `::`.
9#[must_use]
10pub fn normalize_package_separator(module_name: &str) -> Cow<'_, str> {
11    crate::name::normalize_package_separator(module_name)
12}
13
14/// Convert a module name into a relative Perl module path.
15#[must_use]
16pub fn module_name_to_path(module_name: &str) -> String {
17    let normalized = normalize_package_separator(module_name);
18    format!("{}.pm", normalized.replace("::", "/"))
19}
20
21/// Convert a module path/key into a module name.
22///
23/// Handles both `/` and `\\` separators and strips `.pm`/`.pl` suffixes.
24#[must_use]
25pub fn module_path_to_name(module_path: &str) -> String {
26    let normalized = module_path.replace('\\', "/");
27    let without_ext = strip_perl_extension(&normalized);
28    without_ext.replace('/', "::")
29}
30
31/// Convert a filesystem source path into a likely module name.
32///
33/// Rules:
34/// 1. Strip `.pm` or `.pl` suffix
35/// 2. If a `lib/` segment exists, use everything after the last `lib/`
36/// 3. Otherwise, fall back to the file stem
37#[must_use]
38pub fn file_path_to_module_name(file_path: &str) -> String {
39    let normalized = file_path.replace('\\', "/");
40    let without_ext = strip_perl_extension(&normalized);
41
42    if let Some(relative_module_path) = strip_to_lib_relative_path(without_ext) {
43        return module_path_to_name(relative_module_path);
44    }
45
46    without_ext
47        .rsplit('/')
48        .next()
49        .filter(|segment| !segment.is_empty())
50        .unwrap_or(without_ext)
51        .to_string()
52}
53
54fn strip_to_lib_relative_path(path: &str) -> Option<&str> {
55    if let Some(stripped) = path.strip_prefix("lib/") {
56        return Some(stripped);
57    }
58
59    path.rfind("/lib/").map(|lib_idx| &path[lib_idx + "/lib/".len()..])
60}
61
62fn strip_perl_extension(path: &str) -> &str {
63    if let Some(stripped) = path.strip_suffix(".pm") {
64        stripped
65    } else if let Some(stripped) = path.strip_suffix(".pl") {
66        stripped
67    } else {
68        path
69    }
70}