Skip to main content

mago_codex/
signature.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_atom::Atom;
5
6/// Represents a signature node for a definition (function, class, method, constant, etc.).
7///
8/// This structure forms a hierarchical tree where top-level symbols (classes, functions)
9/// can have children (methods, properties within classes).
10///
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct DefSignatureNode {
13    /// The name of the symbol (e.g., "Foo" for class Foo, "bar" for method bar)
14    pub name: Atom,
15
16    /// Whether this node represents a function or method
17    pub is_function: bool,
18
19    /// Whether this node represents a constant
20    pub is_constant: bool,
21
22    /// Starting byte offset in the source file
23    pub start_offset: u32,
24
25    /// Ending byte offset in the source file
26    pub end_offset: u32,
27
28    /// Starting line number (1-indexed)
29    pub start_line: u32,
30
31    /// Ending line number (1-indexed)
32    pub end_line: u32,
33
34    /// Starting column (0-indexed)
35    pub start_column: u16,
36
37    /// Ending column (0-indexed)
38    pub end_column: u16,
39
40    /// Nested symbols (e.g., methods and properties within a class)
41    pub children: Vec<DefSignatureNode>,
42
43    /// Position-insensitive fingerprint hash covering the entire definition.
44    /// Any change to signature, body, modifiers, or attributes will change this hash.
45    pub hash: u64,
46
47    /// Signature-only fingerprint hash, excluding function/method bodies.
48    /// Used by the differ to determine cascade invalidation: if only the body changed
49    /// (signature_hash unchanged), dependents are not invalidated — only the changed
50    /// file itself is re-analyzed.
51    pub signature_hash: u64,
52}
53
54impl DefSignatureNode {
55    /// Creates a new `DefSignatureNode` with the given parameters.
56    #[inline]
57    #[allow(clippy::too_many_arguments)]
58    #[must_use]
59    pub fn new(
60        name: Atom,
61        is_function: bool,
62        is_constant: bool,
63        start_offset: u32,
64        end_offset: u32,
65        start_line: u32,
66        end_line: u32,
67        start_column: u16,
68        end_column: u16,
69        hash: u64,
70        signature_hash: u64,
71    ) -> Self {
72        Self {
73            name,
74            is_function,
75            is_constant,
76            start_offset,
77            end_offset,
78            start_line,
79            end_line,
80            start_column,
81            end_column,
82            children: Vec::new(),
83            hash,
84            signature_hash,
85        }
86    }
87
88    /// Adds a child node to this definition.
89    #[inline]
90    pub fn add_child(&mut self, child: DefSignatureNode) {
91        self.children.push(child);
92    }
93
94    /// Returns a reference to the children of this node.
95    #[inline]
96    #[must_use]
97    pub fn children(&self) -> &[DefSignatureNode] {
98        &self.children
99    }
100
101    /// Returns a mutable reference to the children of this node.
102    #[inline]
103    pub fn children_mut(&mut self) -> &mut Vec<DefSignatureNode> {
104        &mut self.children
105    }
106}
107
108/// Represents the signature of an entire file.
109///
110/// This contains all top-level definitions (classes, interfaces, traits, enums,
111/// functions, constants) in the file as a flat vector. Nested definitions
112/// (methods, properties) are stored within the `children` of their parent nodes.
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
114pub struct FileSignature {
115    pub hash: u64,
116    pub ast_nodes: Vec<DefSignatureNode>,
117}
118
119impl FileSignature {
120    /// Creates a new empty `FileSignature`.
121    #[inline]
122    #[must_use]
123    pub fn new(hash: u64) -> Self {
124        Self { hash, ast_nodes: Vec::new() }
125    }
126
127    /// Adds a top-level definition node to this file signature.
128    #[inline]
129    pub fn add_node(&mut self, node: DefSignatureNode) {
130        self.ast_nodes.push(node);
131    }
132
133    /// Returns a reference to the top-level nodes.
134    #[inline]
135    #[must_use]
136    pub fn nodes(&self) -> &[DefSignatureNode] {
137        &self.ast_nodes
138    }
139
140    /// Returns a mutable reference to the top-level nodes.
141    #[inline]
142    pub fn nodes_mut(&mut self) -> &mut Vec<DefSignatureNode> {
143        &mut self.ast_nodes
144    }
145
146    /// Returns true if this file signature has no nodes.
147    #[inline]
148    #[must_use]
149    pub fn is_empty(&self) -> bool {
150        self.ast_nodes.is_empty()
151    }
152
153    /// Returns the number of top-level nodes.
154    #[inline]
155    #[must_use]
156    pub fn len(&self) -> usize {
157        self.ast_nodes.len()
158    }
159}