mago_codex/symbol.rs
1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_atom::Atom;
5use mago_atom::AtomMap;
6
7/// A pair of `Atom`s representing a symbol and its member.
8///
9/// This is used to uniquely identify a symbol and its member within the codebase,
10/// where the first `Atom` is the symbol's fully qualified class name (FQCN)
11/// and the second `Atom` is the member's name (e.g., method, property, constant),
12/// or an empty string if the symbol itself is being referenced (e.g., a class or function
13/// without a specific member).
14pub type SymbolIdentifier = (Atom, Atom);
15
16/// Represents the different kinds of top-level class-like structures in PHP.
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
18pub enum SymbolKind {
19 Class,
20 Enum,
21 Trait,
22 Interface,
23}
24
25impl SymbolKind {
26 /// Checks if this symbol kind is `Class`.
27 #[inline]
28 #[must_use]
29 pub const fn is_class(&self) -> bool {
30 matches!(self, SymbolKind::Class)
31 }
32
33 /// Checks if this symbol kind is `Enum`.
34 #[inline]
35 #[must_use]
36 pub const fn is_enum(&self) -> bool {
37 matches!(self, SymbolKind::Enum)
38 }
39
40 /// Checks if this symbol kind is `Trait`.
41 #[inline]
42 #[must_use]
43 pub const fn is_trait(&self) -> bool {
44 matches!(self, SymbolKind::Trait)
45 }
46
47 /// Checks if this symbol kind is `Interface`.
48 #[inline]
49 #[must_use]
50 pub const fn is_interface(&self) -> bool {
51 matches!(self, SymbolKind::Interface)
52 }
53
54 /// Returns the string representation of the symbol kind.
55 #[inline]
56 #[must_use]
57 pub const fn as_str(&self) -> &'static str {
58 match self {
59 SymbolKind::Class => "class",
60 SymbolKind::Enum => "enum",
61 SymbolKind::Trait => "trait",
62 SymbolKind::Interface => "interface",
63 }
64 }
65}
66
67/// Stores a map of all known class-like symbol names (FQCNs) to their corresponding `SymbolKind`.
68/// Provides basic methods for adding symbols and querying.
69#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
70pub struct Symbols {
71 all: AtomMap<SymbolKind>,
72}
73
74impl Symbols {
75 /// Creates a new, empty `Symbols` map.
76 #[inline]
77 #[must_use]
78 pub fn new() -> Symbols {
79 Symbols { all: AtomMap::default() }
80 }
81
82 /// Adds or updates a symbol name identified as a `Class`.
83 #[inline]
84 pub fn add_class_name(&mut self, name: Atom) {
85 self.all.insert(name, SymbolKind::Class);
86 }
87
88 /// Adds or updates a symbol name identified as an `Interface`.
89 #[inline]
90 pub fn add_interface_name(&mut self, name: Atom) {
91 self.all.insert(name, SymbolKind::Interface);
92 }
93
94 /// Adds or updates a symbol name identified as a `Trait`.
95 #[inline]
96 pub fn add_trait_name(&mut self, name: Atom) {
97 self.all.insert(name, SymbolKind::Trait);
98 }
99
100 /// Adds or updates a symbol name identified as an `Enum`.
101 #[inline]
102 pub fn add_enum_name(&mut self, name: Atom) {
103 self.all.insert(name, SymbolKind::Enum);
104 }
105
106 /// Retrieves the `SymbolKind` for a given symbol name, if known.
107 ///
108 /// # Arguments
109 ///
110 /// * `name`: The `Atom` (likely FQCN) of the symbol to look up.
111 ///
112 /// # Returns
113 ///
114 /// `Some(SymbolKind)` if the symbol exists in the map, `None` otherwise.
115 #[inline]
116 #[must_use]
117 pub fn get_kind(&self, name: &Atom) -> Option<SymbolKind> {
118 self.all.get(name).copied() // Use copied() since SymbolKind is Copy
119 }
120
121 /// Checks if a symbol with the given name is known.
122 ///
123 /// # Arguments
124 ///
125 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
126 ///
127 /// # Returns
128 ///
129 /// `true` if the symbol exists in the map, `false` otherwise.
130 #[inline]
131 #[must_use]
132 pub fn contains(&self, name: &Atom) -> bool {
133 self.all.contains_key(name)
134 }
135
136 /// Checks if a symbol with the given name is a `Class`.
137 ///
138 /// # Arguments
139 ///
140 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
141 ///
142 /// # Returns
143 ///
144 /// `true` if the symbol is a `Class`, `false` otherwise.
145 #[inline]
146 #[must_use]
147 pub fn contains_class(&self, name: &Atom) -> bool {
148 matches!(self.get_kind(name), Some(SymbolKind::Class))
149 }
150
151 /// Checks if a symbol with the given name is an `Interface`.
152 ///
153 /// # Arguments
154 ///
155 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
156 ///
157 /// # Returns
158 ///
159 /// `true` if the symbol is an `Interface`, `false` otherwise.
160 #[inline]
161 #[must_use]
162 pub fn contains_interface(&self, name: &Atom) -> bool {
163 matches!(self.get_kind(name), Some(SymbolKind::Interface))
164 }
165
166 /// Checks if a symbol with the given name is a `Trait`.
167 ///
168 /// # Arguments
169 ///
170 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
171 ///
172 /// # Returns
173 ///
174 /// `true` if the symbol is a `Trait`, `false` otherwise.
175 #[inline]
176 #[must_use]
177 pub fn contains_trait(&self, name: &Atom) -> bool {
178 matches!(self.get_kind(name), Some(SymbolKind::Trait))
179 }
180
181 /// Checks if a symbol with the given name is an `Enum`.
182 ///
183 /// # Arguments
184 ///
185 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
186 ///
187 /// # Returns
188 ///
189 /// `true` if the symbol is an `Enum`, `false` otherwise.
190 #[inline]
191 #[must_use]
192 pub fn contains_enum(&self, name: &Atom) -> bool {
193 matches!(self.get_kind(name), Some(SymbolKind::Enum))
194 }
195
196 /// Returns a reference to the underlying map of all symbols.
197 #[inline]
198 #[must_use]
199 pub fn get_all(&self) -> &AtomMap<SymbolKind> {
200 &self.all
201 }
202
203 /// Extends the current `Symbols` map with another one.
204 #[inline]
205 pub fn extend(&mut self, other: Symbols) {
206 for (entry, kind) in other.all {
207 self.all.entry(entry).or_insert(kind);
208 }
209 }
210}
211
212/// Provides a default, empty `Symbols` map.
213impl Default for Symbols {
214 #[inline]
215 fn default() -> Self {
216 Self::new()
217 }
218}