Skip to main content

mago_syntax/cst/cst/
identifier.rs

1use strum::Display;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5
6/// Represents an identifier.
7///
8/// An identifier can be a local, qualified, or fully qualified identifier.
9#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize))]
11#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
12pub enum Identifier<'arena> {
13    Local(LocalIdentifier<'arena>),
14    Qualified(QualifiedIdentifier<'arena>),
15    FullyQualified(FullyQualifiedIdentifier<'arena>),
16}
17
18/// Represents a local, unqualified identifier.
19///
20/// Example: `foo`, `Bar`, `BAZ`
21#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct LocalIdentifier<'arena> {
24    pub span: Span,
25    pub value: &'arena [u8],
26}
27
28/// Represents a qualified identifier.
29///
30/// Example: `Foo\bar`, `Bar\Baz`, `Baz\QUX`
31#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct QualifiedIdentifier<'arena> {
34    pub span: Span,
35    pub value: &'arena [u8],
36}
37
38/// Represents a fully qualified identifier.
39///
40/// Example: `\Foo\bar`, `\Bar\Baz`, `\Baz\QUX`
41#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43pub struct FullyQualifiedIdentifier<'arena> {
44    pub span: Span,
45    pub value: &'arena [u8],
46}
47
48impl<'arena> Identifier<'arena> {
49    #[inline]
50    #[must_use]
51    pub const fn is_local(&self) -> bool {
52        matches!(self, Identifier::Local(_))
53    }
54
55    #[inline]
56    #[must_use]
57    pub const fn is_qualified(&self) -> bool {
58        matches!(self, Identifier::Qualified(_))
59    }
60
61    #[inline]
62    #[must_use]
63    pub const fn is_fully_qualified(&self) -> bool {
64        matches!(self, Identifier::FullyQualified(_))
65    }
66
67    #[inline]
68    #[must_use]
69    pub const fn value(&self) -> &'arena [u8] {
70        match &self {
71            Identifier::Local(local_identifier) => local_identifier.value,
72            Identifier::Qualified(qualified_identifier) => qualified_identifier.value,
73            Identifier::FullyQualified(fully_qualified_identifier) => fully_qualified_identifier.value,
74        }
75    }
76
77    #[inline]
78    #[must_use]
79    pub fn last_segment(&self) -> &'arena [u8] {
80        let value = self.value();
81
82        match memchr::memrchr(b'\\', value) {
83            Some(pos) => &value[pos + 1..],
84            None => value,
85        }
86    }
87}
88
89impl HasSpan for Identifier<'_> {
90    fn span(&self) -> Span {
91        match self {
92            Identifier::Local(local) => local.span(),
93            Identifier::Qualified(qualified) => qualified.span(),
94            Identifier::FullyQualified(fully_qualified) => fully_qualified.span(),
95        }
96    }
97}
98
99impl HasSpan for LocalIdentifier<'_> {
100    fn span(&self) -> Span {
101        self.span
102    }
103}
104
105impl HasSpan for QualifiedIdentifier<'_> {
106    fn span(&self) -> Span {
107        self.span
108    }
109}
110
111impl HasSpan for FullyQualifiedIdentifier<'_> {
112    fn span(&self) -> Span {
113        self.span
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn identifier(value: &'static [u8]) -> Identifier<'static> {
122        Identifier::FullyQualified(FullyQualifiedIdentifier { span: Span::dummy(0, value.len() as u32), value })
123    }
124
125    #[test]
126    fn last_segment_returns_the_final_namespace_segment() {
127        assert_eq!(identifier(b"\\Illuminate\\Database\\Eloquent\\Attributes\\Scope").last_segment(), b"Scope");
128        assert_eq!(identifier(b"Foo\\Bar").last_segment(), b"Bar");
129        assert_eq!(identifier(b"\\Foo").last_segment(), b"Foo");
130    }
131
132    #[test]
133    fn last_segment_returns_the_whole_value_when_unqualified() {
134        assert_eq!(identifier(b"Scope").last_segment(), b"Scope");
135    }
136}