1use alloc::format;
19use alloc::string::{String, ToString};
20
21use crate::errors::XmlError;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct LibraryRef {
29 pub library: String,
31 pub name: String,
33}
34
35impl LibraryRef {
36 #[must_use]
38 pub fn is_qualified(&self) -> bool {
39 !self.library.is_empty()
40 }
41}
42
43pub fn parse_library_ref(s: &str) -> Result<LibraryRef, XmlError> {
53 let trimmed = s.trim();
54 if trimmed.is_empty() {
55 return Err(XmlError::UnresolvedReference("empty reference".into()));
56 }
57 if let Some((lib, rest)) = trimmed.split_once("::") {
58 if lib.is_empty() {
59 return Err(XmlError::UnresolvedReference(format!(
60 "empty library segment in `{trimmed}`"
61 )));
62 }
63 if rest.contains("::") {
64 return Err(XmlError::UnresolvedReference(format!(
65 "more than two segments in `{trimmed}`"
66 )));
67 }
68 if rest.is_empty() {
69 return Err(XmlError::UnresolvedReference(format!(
70 "empty name segment in `{trimmed}`"
71 )));
72 }
73 Ok(LibraryRef {
74 library: lib.to_string(),
75 name: rest.to_string(),
76 })
77 } else {
78 Ok(LibraryRef {
79 library: String::new(),
80 name: trimmed.to_string(),
81 })
82 }
83}
84
85#[cfg(test)]
86#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn qualified() {
92 let r = parse_library_ref("lib::name").expect("ok");
93 assert_eq!(r.library, "lib");
94 assert_eq!(r.name, "name");
95 assert!(r.is_qualified());
96 }
97
98 #[test]
99 fn unqualified() {
100 let r = parse_library_ref("name").expect("ok");
101 assert_eq!(r.library, "");
102 assert_eq!(r.name, "name");
103 assert!(!r.is_qualified());
104 }
105
106 #[test]
107 fn empty_rejected() {
108 assert!(matches!(
109 parse_library_ref(""),
110 Err(XmlError::UnresolvedReference(_))
111 ));
112 assert!(matches!(
113 parse_library_ref(" "),
114 Err(XmlError::UnresolvedReference(_))
115 ));
116 }
117
118 #[test]
119 fn empty_segment_rejected() {
120 assert!(matches!(
121 parse_library_ref("::name"),
122 Err(XmlError::UnresolvedReference(_))
123 ));
124 assert!(matches!(
125 parse_library_ref("lib::"),
126 Err(XmlError::UnresolvedReference(_))
127 ));
128 }
129
130 #[test]
131 fn three_segments_rejected() {
132 assert!(matches!(
133 parse_library_ref("a::b::c"),
134 Err(XmlError::UnresolvedReference(_))
135 ));
136 }
137}