morphir_core/naming/
qname.rs1use crate::naming::{name::Name, path::Path};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
7#[serde(try_from = "String", into = "String")]
8pub struct QName {
9 pub module_path: Path,
10 pub local_name: Name,
11}
12
13impl QName {
14 pub fn new(module_path: Path, local_name: Name) -> Self {
15 Self {
16 module_path,
17 local_name,
18 }
19 }
20
21 pub fn parse(s: &str) -> Option<Self> {
22 let parts: Vec<&str> = s.split(':').collect();
23 if parts.len() != 2 {
24 return None;
25 }
26 let path_str = parts[0];
27 let name_str = parts[1];
28 if name_str.is_empty() {
30 return None;
31 }
32 Some(Self::new(Path::new(path_str), Name::from(name_str)))
33 }
34}
35
36impl std::fmt::Display for QName {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 write!(f, "{}:{}", self.module_path, self.local_name)
39 }
40}
41
42impl From<QName> for String {
43 fn from(qname: QName) -> String {
44 qname.to_string()
45 }
46}
47
48impl TryFrom<String> for QName {
49 type Error = String;
50 fn try_from(s: String) -> Result<Self, Self::Error> {
51 QName::parse(&s).ok_or_else(|| format!("Invalid QName string: {}", s))
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_qname_parsing() {
61 let q = QName::parse("foo/bar:Baz").unwrap();
62 assert_eq!(q.module_path.to_string(), "foo/bar");
63 assert_eq!(q.local_name.to_kebab_case(), "baz");
64 }
65
66 #[test]
67 fn test_qname_roundtrip() {
68 let q = QName::parse("mypkg/mymod:MyFunc").unwrap();
69 let s = q.to_string();
70 assert_eq!(s, "mypkg/mymod:my-func");
71 }
72}