cuc/
namespace.rs

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        // assert_eq!(parent_scope("a_b_c_"), "a_b");
86        // assert_eq!(parent_scope("a_b_c"), "a_b");
87        // assert_eq!(parent_scope("a_"), ""); // only one level
88        // assert_eq!(parent_scope("abc"), ""); // no underscores
89    }
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()); // Empty string should not be added
214        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()); // Whitespace-only should not be added
221        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  "]); // trim() only checks, doesn't modify
228    }
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}