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}