Skip to main content

mago_codex/identifier/
function_like.rs

1use mago_database::file::FileId;
2use serde::Deserialize;
3use serde::Serialize;
4
5use mago_atom::Atom;
6use mago_span::Position;
7
8use crate::identifier::method::MethodIdentifier;
9
10/// Identifies a specific function-like construct within the codebase.
11///
12/// This distinguishes between globally/namespaced defined functions, methods within
13/// class-like structures, and closures identified by their source position.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
15pub enum FunctionLikeIdentifier {
16    /// A globally or namespaced defined function.
17    /// * `Atom` - The fully qualified name (FQN) of the function.
18    Function(Atom),
19    /// A method within a class, interface, trait, or enum.
20    /// * `Atom` - The fully qualified class name (FQCN) of the containing structure.
21    /// * `Atom` - The name of the method.
22    Method(Atom, Atom),
23    /// A closure (anonymous function `function() {}` or arrow function `fn() => expr`).
24    ///
25    /// * `FileId` - The identifier of the file where the closure is defined.
26    /// * `Position` - The starting position of the closure definition.
27    Closure(FileId, Position),
28}
29
30impl FunctionLikeIdentifier {
31    /// Checks if this identifier represents a `Function`.
32    #[inline]
33    #[must_use]
34    pub const fn is_function(&self) -> bool {
35        matches!(self, FunctionLikeIdentifier::Function(_))
36    }
37
38    /// Checks if this identifier represents a `Method`.
39    #[inline]
40    #[must_use]
41    pub const fn is_method(&self) -> bool {
42        matches!(self, FunctionLikeIdentifier::Method(_, _))
43    }
44
45    /// Checks if this identifier represents a `Closure`.
46    #[inline]
47    #[must_use]
48    pub const fn is_closure(&self) -> bool {
49        matches!(self, FunctionLikeIdentifier::Closure(_, _))
50    }
51
52    /// If this identifier represents a method, returns it as a `MethodIdentifier`.
53    /// Otherwise, returns `None`.
54    #[inline]
55    #[must_use]
56    pub const fn as_method_identifier(&self) -> Option<MethodIdentifier> {
57        match self {
58            FunctionLikeIdentifier::Method(fq_classlike_name, method_name) => {
59                Some(MethodIdentifier::new(*fq_classlike_name, *method_name))
60            }
61            _ => None,
62        }
63    }
64
65    /// Returns a string representation of the kind of function-like construct.
66    #[inline]
67    #[must_use]
68    pub const fn title_kind_str(&self) -> &'static str {
69        match self {
70            FunctionLikeIdentifier::Function(_) => "Function",
71            FunctionLikeIdentifier::Method(_, _) => "Method",
72            FunctionLikeIdentifier::Closure(_, _) => "Closure",
73        }
74    }
75
76    /// Returns a string representation of the kind of function-like construct.
77    #[inline]
78    #[must_use]
79    pub const fn kind_str(&self) -> &'static str {
80        match self {
81            FunctionLikeIdentifier::Function(_) => "function",
82            FunctionLikeIdentifier::Method(_, _) => "method",
83            FunctionLikeIdentifier::Closure(_, _) => "closure",
84        }
85    }
86
87    /// Converts the identifier to a human-readable string representation.
88    ///
89    /// For closures, this typically includes the filename and starting offset.
90    #[inline]
91    #[must_use]
92    pub fn as_string(&self) -> String {
93        match self {
94            FunctionLikeIdentifier::Function(fn_name) => fn_name.to_string(),
95            FunctionLikeIdentifier::Method(fq_classlike_name, method_name) => {
96                format!("{fq_classlike_name}::{method_name}")
97            }
98            FunctionLikeIdentifier::Closure(file_id, position) => {
99                format!("{}:{}", file_id, position.offset)
100            }
101        }
102    }
103
104    /// Creates a stable string representation suitable for use as a key or unique ID.
105    #[inline]
106    #[must_use]
107    pub fn to_hash(&self) -> String {
108        match self {
109            FunctionLikeIdentifier::Function(fn_name) => fn_name.to_string(),
110            FunctionLikeIdentifier::Method(fq_classlike_name, method_name) => {
111                format!("{fq_classlike_name}::{method_name}")
112            }
113            FunctionLikeIdentifier::Closure(file_id, position) => {
114                format!("{}::{}", file_id, position.offset)
115            }
116        }
117    }
118}
119
120impl From<MethodIdentifier> for FunctionLikeIdentifier {
121    #[inline]
122    fn from(value: MethodIdentifier) -> Self {
123        FunctionLikeIdentifier::Method(*value.get_class_name(), *value.get_method_name())
124    }
125}