Skip to main content

morphir_core/naming/
module_name.rs

1use super::Path;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use std::convert::Infallible;
5use std::fmt;
6use std::str::FromStr;
7
8/// ModuleName is a newtype wrapper around Path for type safety.
9/// It distinguishes module paths from package paths at the type level.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
11#[serde(transparent)]
12pub struct ModuleName(pub Path);
13
14impl ModuleName {
15    /// Create a new ModuleName from a Path
16    pub fn new(path: Path) -> Self {
17        Self(path)
18    }
19
20    /// Create a ModuleName from a string (e.g., "Module/SubModule")
21    ///
22    /// This is a convenience wrapper around `FromStr::from_str`.
23    pub fn parse(s: &str) -> Self {
24        Self(Path::new(s))
25    }
26
27    /// Get the underlying Path
28    pub fn as_path(&self) -> &Path {
29        &self.0
30    }
31
32    /// Get the underlying Path. Equivalent to [`ModuleName::as_path`].
33    pub fn path(&self) -> &Path {
34        &self.0
35    }
36
37    /// Convert to the underlying Path
38    pub fn into_path(self) -> Path {
39        self.0
40    }
41
42    /// Check if the module name is empty
43    pub fn is_empty(&self) -> bool {
44        self.0.is_empty()
45    }
46
47    /// Render using [`super::CANONICAL_STYLE`].
48    pub fn to_canonical_string(&self) -> String {
49        self.0.to_canonical_string()
50    }
51
52    /// Parse a canonical module path in either encoding.
53    pub fn from_canonical_string(source: &str) -> Result<Self, String> {
54        Path::from_canonical_string(source).map(Self)
55    }
56}
57
58impl fmt::Display for ModuleName {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "{}", self.0)
61    }
62}
63
64impl From<Path> for ModuleName {
65    fn from(path: Path) -> Self {
66        Self(path)
67    }
68}
69
70impl From<ModuleName> for Path {
71    fn from(module: ModuleName) -> Self {
72        module.0
73    }
74}
75
76impl FromStr for ModuleName {
77    type Err = Infallible;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        Ok(Self(Path::new(s)))
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_module_name_from_str() {
90        let module = ModuleName::parse("Test/Module");
91        assert_eq!(module.to_string(), "test/module");
92    }
93
94    #[test]
95    fn test_module_name_equality() {
96        let m1 = ModuleName::parse("my/module");
97        let m2 = ModuleName::parse("my/module");
98        assert_eq!(m1, m2);
99    }
100
101    #[test]
102    fn test_module_name_from_str_trait() {
103        let module: ModuleName = "Test/Module".parse().unwrap();
104        assert_eq!(module.to_string(), "test/module");
105    }
106
107    #[test]
108    fn test_module_name_from_path() {
109        let path = Path::new("test/mod");
110        let module = ModuleName::from(path.clone());
111        assert_eq!(module.as_path(), &path);
112    }
113}