mago_syntax/cst/cst/
identifier.rs1use strum::Display;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5
6#[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#[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#[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#[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}