dissolve_python/
domain_types.rs1use serde::{Deserialize, Serialize};
18use std::fmt::{Display, Formatter, Result as FmtResult};
19use std::str::FromStr;
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub struct ModuleName(String);
24
25impl ModuleName {
26 pub fn new(name: impl Into<String>) -> Self {
27 Self(name.into())
28 }
29
30 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33
34 pub fn into_string(self) -> String {
35 self.0
36 }
37
38 pub fn is_parent_of(&self, other: &ModuleName) -> bool {
40 other.0.starts_with(&self.0)
41 && other.0.len() > self.0.len()
42 && other.0.chars().nth(self.0.len()) == Some('.')
43 }
44
45 pub fn parent(&self) -> Option<ModuleName> {
47 self.0
48 .rfind('.')
49 .map(|pos| ModuleName(self.0[..pos].to_string()))
50 }
51}
52
53impl Display for ModuleName {
54 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
55 write!(f, "{}", self.0)
56 }
57}
58
59impl FromStr for ModuleName {
60 type Err = &'static str;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 if s.is_empty() {
64 Err("Module name cannot be empty")
65 } else {
66 Ok(ModuleName(s.to_string()))
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
73pub struct FunctionName(String);
74
75impl FunctionName {
76 pub fn new(name: impl Into<String>) -> Self {
77 Self(name.into())
78 }
79
80 pub fn as_str(&self) -> &str {
81 &self.0
82 }
83
84 pub fn into_string(self) -> String {
85 self.0
86 }
87}
88
89impl Display for FunctionName {
90 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
91 write!(f, "{}", self.0)
92 }
93}
94
95impl FromStr for FunctionName {
96 type Err = &'static str;
97
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 if s.is_empty() {
100 Err("Function name cannot be empty")
101 } else {
102 Ok(FunctionName(s.to_string()))
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
109pub struct QualifiedName {
110 pub module: ModuleName,
111 pub function: FunctionName,
112}
113
114impl QualifiedName {
115 pub fn new(module: ModuleName, function: FunctionName) -> Self {
116 Self { module, function }
117 }
118
119 pub fn from_string(qualified_name: &str) -> Result<Self, &'static str> {
120 if let Some(last_dot) = qualified_name.rfind('.') {
121 let module_part = &qualified_name[..last_dot];
122 let function_part = &qualified_name[last_dot + 1..];
123
124 Ok(QualifiedName {
125 module: ModuleName::new(module_part),
126 function: FunctionName::new(function_part),
127 })
128 } else {
129 Err("Qualified name must contain at least one dot")
130 }
131 }
132
133 pub fn to_string(&self) -> String {
134 format!("{}.{}", self.module, self.function)
135 }
136}
137
138impl Display for QualifiedName {
139 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
140 write!(f, "{}.{}", self.module, self.function)
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Hash)]
146pub struct SourcePath(std::path::PathBuf);
147
148impl SourcePath {
149 pub fn new(path: impl Into<std::path::PathBuf>) -> Self {
150 Self(path.into())
151 }
152
153 pub fn as_path(&self) -> &std::path::Path {
154 &self.0
155 }
156
157 pub fn into_path_buf(self) -> std::path::PathBuf {
158 self.0
159 }
160
161 pub fn to_string_lossy(&self) -> std::borrow::Cow<str> {
162 self.0.to_string_lossy()
163 }
164}
165
166impl Display for SourcePath {
167 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
168 write!(f, "{}", self.0.display())
169 }
170}
171
172pub use crate::core::types::Version;
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_module_name_parent() {
181 let module = ModuleName::new("parent.child.grandchild");
182 let parent = module.parent().unwrap();
183 assert_eq!(parent.as_str(), "parent.child");
184
185 let grandparent = parent.parent().unwrap();
186 assert_eq!(grandparent.as_str(), "parent");
187
188 assert!(grandparent.parent().is_none());
189 }
190
191 #[test]
192 fn test_module_is_parent_of() {
193 let parent = ModuleName::new("parent");
194 let child = ModuleName::new("parent.child");
195 let unrelated = ModuleName::new("other");
196
197 assert!(parent.is_parent_of(&child));
198 assert!(!parent.is_parent_of(&unrelated));
199 assert!(!child.is_parent_of(&parent));
200 }
201
202 #[test]
203 fn test_qualified_name_from_string() {
204 let qname = QualifiedName::from_string("module.submodule.function").unwrap();
205 assert_eq!(qname.module.as_str(), "module.submodule");
206 assert_eq!(qname.function.as_str(), "function");
207
208 assert!(QualifiedName::from_string("nomodule").is_err());
209 }
210}