Skip to main content

mago_codex/
signature.rs

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