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}