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 pub const fn is_class(&self) -> bool {
29 matches!(self, SymbolKind::Class)
30 }
31
32 /// Checks if this symbol kind is `Enum`.
33 #[inline]
34 pub const fn is_enum(&self) -> bool {
35 matches!(self, SymbolKind::Enum)
36 }
37
38 /// Checks if this symbol kind is `Trait`.
39 #[inline]
40 pub const fn is_trait(&self) -> bool {
41 matches!(self, SymbolKind::Trait)
42 }
43
44 /// Checks if this symbol kind is `Interface`.
45 #[inline]
46 pub const fn is_interface(&self) -> bool {
47 matches!(self, SymbolKind::Interface)
48 }
49
50 /// Returns the string representation of the symbol kind.
51 #[inline]
52 pub const fn as_str(&self) -> &'static str {
53 match self {
54 SymbolKind::Class => "class",
55 SymbolKind::Enum => "enum",
56 SymbolKind::Trait => "trait",
57 SymbolKind::Interface => "interface",
58 }
59 }
60}
61
62/// Stores a map of all known class-like symbol names (FQCNs) to their corresponding `SymbolKind`.
63/// Provides basic methods for adding symbols and querying.
64#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
65pub struct Symbols {
66 all: AtomMap<SymbolKind>,
67}
68
69impl Symbols {
70 /// Creates a new, empty `Symbols` map.
71 #[inline]
72 pub fn new() -> Symbols {
73 Symbols { all: AtomMap::default() }
74 }
75
76 /// Adds or updates a symbol name identified as a `Class`.
77 #[inline]
78 pub fn add_class_name(&mut self, name: Atom) {
79 self.all.insert(name, SymbolKind::Class);
80 }
81
82 /// Adds or updates a symbol name identified as an `Interface`.
83 #[inline]
84 pub fn add_interface_name(&mut self, name: Atom) {
85 self.all.insert(name, SymbolKind::Interface);
86 }
87
88 /// Adds or updates a symbol name identified as a `Trait`.
89 #[inline]
90 pub fn add_trait_name(&mut self, name: Atom) {
91 self.all.insert(name, SymbolKind::Trait);
92 }
93
94 /// Adds or updates a symbol name identified as an `Enum`.
95 #[inline]
96 pub fn add_enum_name(&mut self, name: Atom) {
97 self.all.insert(name, SymbolKind::Enum);
98 }
99
100 /// Retrieves the `SymbolKind` for a given symbol name, if known.
101 ///
102 /// # Arguments
103 ///
104 /// * `name`: The `Atom` (likely FQCN) of the symbol to look up.
105 ///
106 /// # Returns
107 ///
108 /// `Some(SymbolKind)` if the symbol exists in the map, `None` otherwise.
109 #[inline]
110 pub fn get_kind(&self, name: &Atom) -> Option<SymbolKind> {
111 self.all.get(name).copied() // Use copied() since SymbolKind is Copy
112 }
113
114 /// Checks if a symbol with the given name is known.
115 ///
116 /// # Arguments
117 ///
118 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
119 ///
120 /// # Returns
121 ///
122 /// `true` if the symbol exists in the map, `false` otherwise.
123 #[inline]
124 pub fn contains(&self, name: &Atom) -> bool {
125 self.all.contains_key(name)
126 }
127
128 /// Checks if a symbol with the given name is a `Class`.
129 ///
130 /// # Arguments
131 ///
132 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
133 ///
134 /// # Returns
135 ///
136 /// `true` if the symbol is a `Class`, `false` otherwise.
137 #[inline]
138 pub fn contains_class(&self, name: &Atom) -> bool {
139 matches!(self.get_kind(name), Some(SymbolKind::Class))
140 }
141
142 /// Checks if a symbol with the given name is an `Interface`.
143 ///
144 /// # Arguments
145 ///
146 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
147 ///
148 /// # Returns
149 ///
150 /// `true` if the symbol is an `Interface`, `false` otherwise.
151 #[inline]
152 pub fn contains_interface(&self, name: &Atom) -> bool {
153 matches!(self.get_kind(name), Some(SymbolKind::Interface))
154 }
155
156 /// Checks if a symbol with the given name is a `Trait`.
157 ///
158 /// # Arguments
159 ///
160 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
161 ///
162 /// # Returns
163 ///
164 /// `true` if the symbol is a `Trait`, `false` otherwise.
165 #[inline]
166 pub fn contains_trait(&self, name: &Atom) -> bool {
167 matches!(self.get_kind(name), Some(SymbolKind::Trait))
168 }
169
170 /// Checks if a symbol with the given name is an `Enum`.
171 ///
172 /// # Arguments
173 ///
174 /// * `name`: The `Atom` (likely FQCN) of the symbol to check.
175 ///
176 /// # Returns
177 ///
178 /// `true` if the symbol is an `Enum`, `false` otherwise.
179 #[inline]
180 pub fn contains_enum(&self, name: &Atom) -> bool {
181 matches!(self.get_kind(name), Some(SymbolKind::Enum))
182 }
183
184 /// Returns a reference to the underlying map of all symbols.
185 #[inline]
186 pub fn get_all(&self) -> &AtomMap<SymbolKind> {
187 &self.all
188 }
189
190 /// Extends the current `Symbols` map with another one.
191 #[inline]
192 pub fn extend(&mut self, other: Symbols) {
193 for (entry, kind) in other.all {
194 self.all.entry(entry).or_insert(kind);
195 }
196 }
197}
198
199/// Provides a default, empty `Symbols` map.
200impl Default for Symbols {
201 #[inline]
202 fn default() -> Self {
203 Self::new()
204 }
205}