1use blake3::Hasher;
8use serde::{Deserialize, Serialize};
9use std::path::{Path, PathBuf};
10
11#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct NodeId([u8; 16]);
14
15impl NodeId {
16 pub fn new(repo_id: &str, file_path: &Path, span: &Span, kind: &NodeKind) -> Self {
18 let mut hasher = Hasher::new();
19 hasher.update(repo_id.as_bytes());
20 hasher.update(file_path.to_string_lossy().as_bytes());
21 hasher.update(&span.start_byte.to_le_bytes());
22 hasher.update(&span.end_byte.to_le_bytes());
23 hasher.update(format!("{:?}", kind).as_bytes());
24
25 let hash = hasher.finalize();
26 let mut id = [0u8; 16];
27 id.copy_from_slice(&hash.as_bytes()[..16]);
28 Self(id)
29 }
30
31 pub fn to_hex(&self) -> String {
33 hex::encode(self.0)
34 }
35}
36
37impl std::fmt::Debug for NodeId {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "NodeId({})", &self.to_hex()[..8])
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum NodeKind {
47 Module,
49 Class,
51 Function,
53 Method,
55 Parameter,
57 Variable,
59 Call,
61 Import,
63 Literal,
65 Route,
67 SqlQuery,
69 Event,
71
72 Trait,
75 Impl,
77 Struct,
79 Enum,
81 Mod,
83 Macro,
85 Lifetime,
87 Use,
89 Pub,
91 Const,
93 Static,
95 TypeAlias,
97 Union,
99 AssociatedType,
101 AssociatedConst,
103 Field,
105 Variant,
107 Attribute,
109
110 Unknown,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
116#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
117pub enum EdgeKind {
118 Calls,
120 Reads,
122 Writes,
124 Imports,
126 Emits,
128 RoutesTo,
130 Raises,
132 Extends,
134 Implements,
136
137 ImplementsTrait,
140 Uses,
142 Derives,
144 Constrains,
146 Owns,
148 Borrows,
150 BorrowsMut,
152 Outlives,
154 Bounds,
156 Binds,
158 Expands,
160 Contains,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
166pub struct Span {
167 pub start_byte: usize,
169 pub end_byte: usize,
171 pub start_line: usize,
173 pub end_line: usize,
175 pub start_column: usize,
177 pub end_column: usize,
179}
180
181impl Span {
182 pub fn new(
184 start_byte: usize,
185 end_byte: usize,
186 start_line: usize,
187 end_line: usize,
188 start_column: usize,
189 end_column: usize,
190 ) -> Self {
191 Self {
192 start_byte,
193 end_byte,
194 start_line,
195 end_line,
196 start_column,
197 end_column,
198 }
199 }
200
201 pub fn from_node(node: &tree_sitter::Node) -> Self {
203 let start_pos = node.start_position();
204 let end_pos = node.end_position();
205
206 Self {
207 start_byte: node.start_byte(),
208 end_byte: node.end_byte(),
209 start_line: start_pos.row + 1, end_line: end_pos.row + 1,
211 start_column: start_pos.column + 1,
212 end_column: end_pos.column + 1,
213 }
214 }
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
219#[serde(rename_all = "lowercase")]
220pub enum Language {
221 Rust,
223 Python,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct Node {
230 pub id: NodeId,
232 pub kind: NodeKind,
234 pub name: String,
236 pub lang: Language,
238 pub file: PathBuf,
240 pub span: Span,
242 pub signature: Option<String>,
244 pub metadata: serde_json::Value,
246}
247
248impl Node {
249 pub fn new(
251 repo_id: &str,
252 kind: NodeKind,
253 name: String,
254 lang: Language,
255 file: PathBuf,
256 span: Span,
257 ) -> Self {
258 let id = NodeId::new(repo_id, &file, &span, &kind);
259 Self {
260 id,
261 kind,
262 name,
263 lang,
264 file,
265 span,
266 signature: None,
267 metadata: serde_json::Value::Null,
268 }
269 }
270
271 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
273 self.metadata = metadata;
274 self
275 }
276
277 pub fn with_signature(mut self, signature: String) -> Self {
279 self.signature = Some(signature);
280 self
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
286pub struct Edge {
287 pub source: NodeId,
289 pub target: NodeId,
291 pub kind: EdgeKind,
293}
294
295impl Edge {
296 pub fn new(source: NodeId, target: NodeId, kind: EdgeKind) -> Self {
298 Self {
299 source,
300 target,
301 kind,
302 }
303 }
304}