mago_codex/
misc.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_interner::StringIdentifier;
5use mago_interner::ThreadedInterner;
6use mago_span::Span;
7
8/// Represents a PHP variable identifier (e.g., `$foo`, `$this`).
9/// Wraps a `StringIdentifier` which holds the interned name (including '$').
10#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
11pub struct VariableIdentifier(
12    /// The interned identifier for the variable name (e.g., "$foo").
13    pub StringIdentifier,
14);
15
16/// Identifies the target of an expression, distinguishing simple variables from property accesses.
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
18pub enum ExpressionIdentifier {
19    /// A simple variable identifier.
20    ///
21    /// * `VariableIdentifier` - The identifier for the variable (e.g., `$foo`).
22    Variable(VariableIdentifier),
23    /// An instance property access (e.g., `$this->prop`, `$user->name`).
24    ///
25    /// * `VariableIdentifier` - The identifier for the object variable (e.g., `$this`, `$user`).
26    /// * `Span` - The source code location covering the property name part (e.g., `prop` or `name`).
27    /// * `StringIdentifier` - The name of the property being accessed (e.g., `prop`, `name`).
28    InstanceProperty(VariableIdentifier, Span, StringIdentifier),
29}
30
31/// Identifies the scope where a generic template parameter (`@template`) is defined.
32#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, Debug)]
33pub enum GenericParent {
34    /// The template is defined on a class, interface, trait, or enum.
35    /// * `StringIdentifier` - The fully qualified name (FQCN) of the class-like structure.
36    ClassLike(StringIdentifier),
37    /// The template is defined on a function or method.
38    /// * `(StringIdentifier, StringIdentifier)` - A tuple representing the function/method.
39    ///   - `.0`: The FQCN of the class if it's a method, or the FQN of the function if global/namespaced.
40    ///   - `.1`: The method name if it's a method, or `StringIdentifier::empty()` if it's a function.
41    FunctionLike((StringIdentifier, StringIdentifier)),
42}
43
44impl GenericParent {
45    /// Converts the `GenericParent` identifier to a stable string representation,
46    /// optionally using an interner for human-readable names.
47    ///
48    /// Used for creating unique keys or debugging information related to generic contexts.
49    /// The format distinguishes between class-like (`Namespace\ClassName`) and function-like
50    /// parents (`fn-Namespace\functionName` or `fn-Namespace\ClassName::methodName`).
51    ///
52    /// # Arguments
53    ///
54    /// * `interner` - An optional reference to the `ThreadedInterner` for resolving names.
55    #[inline]
56    pub fn to_string(&self, interner: Option<&ThreadedInterner>) -> String {
57        match self {
58            GenericParent::ClassLike(id) => {
59                if let Some(interner) = interner {
60                    interner.lookup(id).to_string()
61                } else {
62                    id.to_string()
63                }
64            }
65            GenericParent::FunctionLike(id) => {
66                let part1 = id.0;
67                let part2 = id.1;
68
69                if part1.is_empty() {
70                    if let Some(interner) = interner {
71                        format!("{}()", interner.lookup(&part2))
72                    } else {
73                        format!("{part2}()")
74                    }
75                } else if let Some(interner) = interner {
76                    format!("{}::{}()", interner.lookup(&part1), interner.lookup(&part2))
77                } else {
78                    format!("{part1}::{part2}()")
79                }
80            }
81        }
82    }
83}