1extern crate alloc;
12use alloc::string::String;
13use core::fmt;
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serde", serde(transparent))]
18pub struct Namespace(String);
19
20impl Namespace {
21 #[inline]
23 pub fn new(s: impl Into<String>) -> Self {
24 Self(s.into())
25 }
26
27 pub const DEFAULT: &'static str = "local";
29
30 pub fn default_ns() -> Self {
32 Self::new(Self::DEFAULT)
33 }
34
35 #[inline]
36 pub fn as_str(&self) -> &str {
37 &self.0
38 }
39
40 pub fn is_child_of(&self, parent: &Namespace) -> bool {
43 self.0.len() > parent.0.len()
44 && self.0.starts_with(parent.as_str())
45 && self.0.as_bytes().get(parent.0.len()) == Some(&b':')
46 }
47
48 pub fn into_inner(self) -> String {
49 self.0
50 }
51}
52
53impl Default for Namespace {
54 fn default() -> Self {
55 Self::default_ns()
56 }
57}
58
59impl fmt::Display for Namespace {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str(&self.0)
62 }
63}
64
65impl AsRef<str> for Namespace {
66 #[inline]
67 fn as_ref(&self) -> &str {
68 &self.0
69 }
70}
71
72impl From<&str> for Namespace {
73 #[inline]
74 fn from(s: &str) -> Self {
75 Self::new(s)
76 }
77}
78
79impl From<String> for Namespace {
80 #[inline]
81 fn from(s: String) -> Self {
82 Self(s)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn construction() {
92 let ns = Namespace::new("research");
93 assert_eq!(ns.as_str(), "research");
94 }
95
96 #[test]
97 fn default_is_local() {
98 assert_eq!(Namespace::default().as_str(), "local");
99 }
100
101 #[test]
102 fn is_child_of() {
103 let parent = Namespace::new("research");
104 let child = Namespace::new("research:lattice");
105 let sibling = Namespace::new("other");
106
107 assert!(child.is_child_of(&parent));
108 assert!(!sibling.is_child_of(&parent));
109 assert!(!parent.is_child_of(&parent));
110 }
111}