1use std::fmt::Display;
2
3#[derive(Debug, Clone)]
4pub struct NameSpace {
5 scope: Vec<String>,
6}
7
8#[derive(Debug, Clone)]
9pub struct NameSpaceView<'me> {
10 scope: &'me [String],
11}
12
13impl NameSpace {
14 const SEPARATOR: &'static str = "::";
15 const FUNC_SEPARATOR: &'static str = "_";
16
17 pub fn root() -> Self {
18 Self { scope: vec![] }
19 }
20
21 pub fn is_root(&self) -> bool {
22 self.scope.is_empty()
23 }
24
25 pub fn view(&self) -> NameSpaceView {
26 NameSpaceView {
27 scope: self.scope.as_slice(),
28 }
29 }
30
31 pub fn join<S>(mut self, other: S) -> Self
32 where
33 S: Into<String>,
34 {
35 let other = other.into();
36 if !other.trim().is_empty() {
37 self.scope.push(other);
38 }
39 self
40 }
41
42 pub fn parent(&self) -> NameSpaceView {
43 if !self.scope.is_empty() {
44 NameSpaceView {
45 scope: &self.scope[..self.scope.len() - 1],
46 }
47 } else {
48 NameSpaceView { scope: &[] }
49 }
50 }
51
52 pub fn display(&self) -> String {
53 self.scope.join(Self::SEPARATOR)
54 }
55}
56
57impl Default for NameSpace {
58 fn default() -> Self {
59 Self::root()
60 }
61}
62
63impl From<NameSpaceView<'_>> for NameSpace {
64 fn from(value: NameSpaceView) -> Self {
65 NameSpace {
66 scope: value.scope.to_vec(),
67 }
68 }
69}
70
71impl NameSpaceView<'_> {
72 pub fn is_root(&self) -> bool {
73 self.scope.is_empty()
74 }
75
76 pub fn parent(&self) -> NameSpaceView {
77 if !self.scope.is_empty() {
78 NameSpaceView {
79 scope: &self.scope[..self.scope.len() - 1],
80 }
81 } else {
82 NameSpaceView { scope: &[] }
83 }
84
85 }
90
91 pub fn display(&self) -> String {
92 self.scope.join(NameSpace::SEPARATOR)
93 }
94
95 pub fn as_func_str(&self) -> String {
96 self.scope.join(NameSpace::FUNC_SEPARATOR)
97 }
98
99 fn join_func_str(this: &mut String, other: impl AsRef<str>) {
100 let other = other.as_ref();
101 if !other.is_empty() {
102 *this += other;
103 *this += NameSpace::FUNC_SEPARATOR;
104 }
105 }
106
107 pub fn flag_func_name<S>(&self, name: S) -> String
108 where
109 S: AsRef<str>,
110 {
111 let mut func_name = String::from("_flag_");
112 Self::join_func_str(&mut func_name, self.as_func_str());
113 func_name += name.as_ref();
114 func_name
115 }
116
117 pub fn global_flag_func_name(&self) -> String {
118 let mut func_name = String::from("_global_flags_");
119 func_name += &self.as_func_str();
120 func_name
121 }
122
123 pub fn cmd_func_name<S>(&self, name: S) -> String
124 where
125 S: AsRef<str>,
126 {
127 let mut func_name = String::from("_cmd_");
128 Self::join_func_str(&mut func_name, self.as_func_str());
129 func_name += &name.as_ref();
130 func_name
131 }
132}
133
134impl Display for NameSpace {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{}", self.display())
137 }
138}
139
140impl Display for NameSpaceView<'_> {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 write!(f, "{}", self.display())
143 }
144}
145
146pub fn slugify<S>(input: S) -> String
147where
148 S: AsRef<str>,
149{
150 input
151 .as_ref()
152 .chars()
153 .filter(|c| c.is_alphanumeric() || c == &'_')
154 .collect()
155}
156
157pub fn arg_complete_func_name<S>(complete_name: S) -> String
158where
159 S: AsRef<str>,
160{
161 let mut func_name = String::from("_complete_arg_");
162 func_name += &slugify(complete_name.as_ref());
163 func_name
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_constants() {
172 assert_eq!(NameSpace::SEPARATOR, "::");
173 assert_eq!(NameSpace::FUNC_SEPARATOR, "_");
174 }
175
176 #[test]
177 fn test_root() {
178 let ns = NameSpace::root();
179 assert!(ns.scope.is_empty());
180 assert!(ns.is_root());
181 }
182
183 #[test]
184 fn test_is_root() {
185 let root_ns = NameSpace::root();
186 assert!(root_ns.is_root());
187
188 let non_root_ns = NameSpace::root().join("test");
189 assert!(!non_root_ns.is_root());
190 }
191
192 #[test]
193 fn test_view() {
194 let ns = NameSpace::root().join("a").join("b");
195 let view = ns.view();
196 assert_eq!(view.scope, &["a", "b"]);
197
198 let root_ns = NameSpace::root();
199 let root_view = root_ns.view();
200 assert_eq!(root_view.scope, &[] as &[String]);
201 }
202
203 #[test]
204 fn test_join_string() {
205 let ns = NameSpace::root().join("test");
206 assert_eq!(ns.scope, vec!["test"]);
207 assert!(!ns.is_root());
208 }
209
210 #[test]
211 fn test_join_empty_string() {
212 let ns = NameSpace::root().join("");
213 assert!(ns.is_root()); assert_eq!(ns.scope, Vec::<String>::new());
215 }
216
217 #[test]
218 fn test_join_whitespace_only() {
219 let ns = NameSpace::root().join(" ");
220 assert!(ns.is_root()); assert_eq!(ns.scope, Vec::<String>::new());
222 }
223
224 #[test]
225 fn test_join_whitespace_around_content() {
226 let ns = NameSpace::root().join(" test ");
227 assert_eq!(ns.scope, vec![" test "]); }
229
230 #[test]
231 fn test_join_chaining() {
232 let ns = NameSpace::root().join("a").join("b").join("c");
233 assert_eq!(ns.scope, vec!["a", "b", "c"]);
234 }
235
236 #[test]
237 fn test_join_mixed_empty() {
238 let ns = NameSpace::root()
239 .join("a")
240 .join("")
241 .join("b")
242 .join(" ")
243 .join("c");
244 assert_eq!(ns.scope, vec!["a", "b", "c"]);
245 }
246
247 #[test]
248 fn test_parent_empty_scope() {
249 let ns = NameSpace::root();
250 let parent = ns.parent();
251 assert_eq!(parent.scope, &[] as &[String]);
252 }
253
254 #[test]
255 fn test_parent_single_level() {
256 let ns = NameSpace::root().join("a");
257 let parent = ns.parent();
258 assert_eq!(parent.scope, &[] as &[String]);
259 }
260
261 #[test]
262 fn test_parent_multi_level() {
263 let ns = NameSpace::root().join("a").join("b").join("c");
264 let parent = ns.parent();
265 assert_eq!(parent.scope, &["a", "b"]);
266 }
267
268 #[test]
269 fn test_display_empty() {
270 let ns = NameSpace::root();
271 assert_eq!(ns.display(), "");
272 }
273
274 #[test]
275 fn test_display_single() {
276 let ns = NameSpace::root().join("test");
277 assert_eq!(ns.display(), "test");
278 }
279
280 #[test]
281 fn test_display_multiple() {
282 let ns = NameSpace::root().join("a").join("b").join("c");
283 assert_eq!(ns.display(), "a::b::c");
284 }
285
286 #[test]
287 fn test_display_with_special_chars() {
288 let ns = NameSpace::root().join("a_b").join("c::d").join("e-f");
289 assert_eq!(ns.display(), "a_b::c::d::e-f");
290 }
291
292 #[test]
293 fn test_complex_workflow() {
294 let ns = NameSpace::root()
295 .join("module")
296 .join("submodule")
297 .join("function");
298
299 assert!(!ns.is_root());
300 assert_eq!(ns.display(), "module::submodule::function");
301
302 let parent = ns.parent();
303 assert_eq!(parent.scope, &["module", "submodule"]);
304
305 let view = ns.view();
306 assert_eq!(view.scope, &["module", "submodule", "function"]);
307 }
308
309 #[test]
310 fn test_edge_cases() {
311 let ns1 = NameSpace::root().join("\t");
312 assert!(ns1.is_root());
313
314 let ns2 = NameSpace::root().join("\n");
315 assert!(ns2.is_root());
316
317 let ns3 = NameSpace::root().join(" \t \n ");
318 assert!(ns3.is_root());
319 }
320}