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