ddbug_parser/
namespace.rs

1use std::cmp;
2use std::sync::Arc;
3
4/// A namespace kind.
5#[derive(Debug, PartialEq, Eq, Clone, Copy)]
6pub enum NamespaceKind {
7    /// An explicit namespace.
8    Namespace,
9    /// A namespace for items defined within a function.
10    Function,
11    /// A namespace for items defined within a type.
12    Type,
13}
14
15/// A nestable namspace.
16#[derive(Debug)]
17pub struct Namespace<'input> {
18    pub(crate) parent: Option<Arc<Namespace<'input>>>,
19    pub(crate) name: Option<&'input str>,
20    pub(crate) kind: NamespaceKind,
21}
22
23impl<'input> Namespace<'input> {
24    pub(crate) fn new(
25        parent: &Option<Arc<Namespace<'input>>>,
26        name: Option<&'input str>,
27        kind: NamespaceKind,
28    ) -> Arc<Namespace<'input>> {
29        Arc::new(Namespace {
30            parent: parent.clone(),
31            name,
32            kind,
33        })
34    }
35
36    /// The parent namespace.
37    pub fn parent(&self) -> Option<&Namespace<'input>> {
38        self.parent.as_deref()
39    }
40
41    /// The namespace name.
42    #[inline]
43    pub fn name(&self) -> Option<&str> {
44        self.name
45    }
46
47    /// The namespace kind.
48    #[inline]
49    pub fn kind(&self) -> NamespaceKind {
50        self.kind
51    }
52
53    fn len(&self) -> usize {
54        match &self.parent {
55            Some(parent) => parent.len() + 1,
56            None => 1,
57        }
58    }
59
60    fn up(&self, len: usize) -> &Namespace {
61        if len == 0 {
62            self
63        } else {
64            match &self.parent {
65                Some(parent) => parent.up(len - 1),
66                None => self,
67            }
68        }
69    }
70
71    pub(crate) fn is_anon_type(namespace: &Option<Arc<Namespace>>) -> bool {
72        match namespace {
73            Some(namespace) => {
74                namespace.kind == NamespaceKind::Type
75                    && (namespace.name.is_none() || Namespace::is_anon_type(&namespace.parent))
76            }
77            None => false,
78        }
79    }
80
81    fn _is_within<T: AsRef<str>>(&self, namespace: &[T]) -> (bool, usize) {
82        let (ret, offset) = match &self.parent {
83            Some(parent) => parent._is_within(namespace),
84            None => (true, 0),
85        };
86
87        if ret {
88            if offset < namespace.len() {
89                match self.name() {
90                    Some(name) => (name == namespace[offset].as_ref(), offset + 1),
91                    None => (false, offset + 1),
92                }
93            } else {
94                (true, offset)
95            }
96        } else {
97            (false, 0)
98        }
99    }
100
101    /// Return true if this namespace is within the given namespace.
102    ///
103    /// `namespace` is a slice of names, starting with the root namespace name.
104    pub fn is_within<T: AsRef<str>>(&self, namespace: &[T]) -> bool {
105        self._is_within(namespace) == (true, namespace.len())
106    }
107
108    fn _cmp(a: &Namespace, b: &Namespace) -> cmp::Ordering {
109        debug_assert_eq!(a.len(), b.len());
110        match (a.parent.as_ref(), b.parent.as_ref()) {
111            (Some(p1), Some(p2)) => {
112                let ord = Self::_cmp(p1, p2);
113                if ord != cmp::Ordering::Equal {
114                    return ord;
115                }
116            }
117            _ => {}
118        }
119        a.name.cmp(&b.name)
120    }
121
122    fn cmp(a: &Namespace, b: &Namespace) -> cmp::Ordering {
123        let len_a = a.len();
124        let len_b = b.len();
125        match len_a.cmp(&len_b) {
126            cmp::Ordering::Equal => Self::_cmp(a, b),
127            cmp::Ordering::Less => {
128                let b = b.up(len_b - len_a);
129                match Self::_cmp(a, b) {
130                    cmp::Ordering::Equal => cmp::Ordering::Less,
131                    other => other,
132                }
133            }
134            cmp::Ordering::Greater => {
135                let a = a.up(len_a - len_b);
136                match Self::_cmp(a, b) {
137                    cmp::Ordering::Equal => cmp::Ordering::Greater,
138                    other => other,
139                }
140            }
141        }
142    }
143
144    pub(crate) fn cmp_ns_and_name(
145        ns1: Option<&Namespace>,
146        name1: Option<&str>,
147        ns2: Option<&Namespace>,
148        name2: Option<&str>,
149    ) -> cmp::Ordering {
150        match (ns1, ns2) {
151            (Some(ns1), Some(ns2)) => match Namespace::cmp(ns1, ns2) {
152                cmp::Ordering::Equal => name1.cmp(&name2),
153                o => o,
154            },
155            (Some(_), None) => cmp::Ordering::Greater,
156            (None, Some(_)) => cmp::Ordering::Less,
157            (None, None) => name1.cmp(&name2),
158        }
159    }
160}
161
162#[cfg(test)]
163mod test {
164    use super::*;
165
166    #[test]
167    fn cmp() {
168        let ns1 = Namespace::new(&None, Some("a".into()), NamespaceKind::Namespace);
169        let ns2 = Namespace::new(&None, Some("b".into()), NamespaceKind::Namespace);
170        assert_eq!(Namespace::cmp(&ns1, &ns2), cmp::Ordering::Less);
171    }
172}