1use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
12pub enum Language {
13 C,
15 Cpp,
17 CSharp,
19 Css,
21 JavaScript,
23 Python,
25 TypeScript,
27 Rust,
29 Go,
31 Java,
33 Ruby,
35 Php,
37 Swift,
39 Kotlin,
41 Scala,
43 Sql,
45 Dart,
47 Lua,
49 Perl,
51 Shell,
53 Groovy,
55 Elixir,
57 R,
59 Haskell,
61 Html,
63 Svelte,
65 Vue,
67 Zig,
69 Terraform,
71 Puppet,
73 Pulumi,
75 Http,
77 Plsql,
79 Apex,
81 Abap,
83 ServiceNow,
85}
86
87impl fmt::Display for Language {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 match self {
90 Language::C => write!(f, "c"),
91 Language::Cpp => write!(f, "cpp"),
92 Language::CSharp => write!(f, "csharp"),
93 Language::Css => write!(f, "css"),
94 Language::JavaScript => write!(f, "js"),
95 Language::Python => write!(f, "py"),
96 Language::TypeScript => write!(f, "ts"),
97 Language::Rust => write!(f, "rust"),
98 Language::Go => write!(f, "go"),
99 Language::Java => write!(f, "java"),
100 Language::Ruby => write!(f, "ruby"),
101 Language::Php => write!(f, "php"),
102 Language::Swift => write!(f, "swift"),
103 Language::Kotlin => write!(f, "kotlin"),
104 Language::Scala => write!(f, "scala"),
105 Language::Sql => write!(f, "sql"),
106 Language::Dart => write!(f, "dart"),
107 Language::Lua => write!(f, "lua"),
108 Language::Perl => write!(f, "perl"),
109 Language::Shell => write!(f, "shell"),
110 Language::Groovy => write!(f, "groovy"),
111 Language::Elixir => write!(f, "elixir"),
112 Language::R => write!(f, "r"),
113 Language::Haskell => write!(f, "haskell"),
114 Language::Html => write!(f, "html"),
115 Language::Svelte => write!(f, "svelte"),
116 Language::Vue => write!(f, "vue"),
117 Language::Zig => write!(f, "zig"),
118 Language::Terraform => write!(f, "terraform"),
119 Language::Puppet => write!(f, "puppet"),
120 Language::Pulumi => write!(f, "pulumi"),
121 Language::Http => write!(f, "http"),
122 Language::Plsql => write!(f, "plsql"),
123 Language::Apex => write!(f, "apex"),
124 Language::Abap => write!(f, "abap"),
125 Language::ServiceNow => write!(f, "servicenow"),
126 }
127 }
128}
129
130#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
152pub struct NodeId {
153 pub language: Language,
155 pub file: Arc<str>,
157 pub qualified_name: Arc<str>,
160}
161
162impl NodeId {
163 pub fn new(language: Language, file: impl AsRef<str>, qualified_name: impl AsRef<str>) -> Self {
176 Self {
177 language,
178 file: Arc::from(file.as_ref()),
179 qualified_name: Arc::from(qualified_name.as_ref()),
180 }
181 }
182
183 #[must_use]
194 pub fn symbol_name(&self) -> &str {
195 if let Some(name) = self.qualified_name.rsplit("::").next()
197 && name != self.qualified_name.as_ref()
198 {
199 return name;
200 }
201
202 if let Some(name) = self.qualified_name.rsplit('.').next() {
203 return name;
204 }
205
206 &self.qualified_name
207 }
208}
209
210impl fmt::Display for NodeId {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 write!(f, "{}:{}:{}", self.language, self.file, self.qualified_name)
213 }
214}
215
216#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
218pub struct Span {
219 pub start: Position,
221 pub end: Position,
223}
224
225impl Span {
226 #[must_use]
228 pub fn new(start: Position, end: Position) -> Self {
229 Self { start, end }
230 }
231
232 #[must_use]
234 pub fn from_bytes(start: usize, end: usize) -> Self {
235 Self {
236 start: Position {
237 line: 0,
238 column: start,
239 },
240 end: Position {
241 line: 0,
242 column: end,
243 },
244 }
245 }
246}
247
248#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
250pub struct Position {
251 pub line: usize,
253 pub column: usize,
255}
256
257impl Position {
258 #[must_use]
260 pub fn new(line: usize, column: usize) -> Self {
261 Self { line, column }
262 }
263}
264
265#[derive(Debug, Clone, PartialEq)]
267pub enum NodeKind {
268 Function {
270 params: Vec<Param>,
272 return_type: Option<Type>,
274 is_async: bool,
276 },
277 Class {
279 bases: Vec<NodeId>,
281 interfaces: Vec<NodeId>,
283 },
284 Module {
286 exports: Vec<NodeId>,
288 },
289 Variable {
291 var_type: Option<Type>,
293 },
294}
295
296#[derive(Debug, Clone, PartialEq)]
298pub struct Param {
299 pub name: String,
301 pub param_type: Option<Type>,
303}
304
305#[derive(Debug, Clone, PartialEq)]
307pub struct Type {
308 pub name: String,
310}
311
312#[derive(Debug, Clone, Default)]
314pub struct NodeMetadata {
315 pub visibility: Option<String>,
317 pub doc_comment: Option<String>,
319 pub attributes: Vec<String>,
321}
322
323#[derive(Debug, Clone)]
325pub struct CodeNode {
326 pub id: NodeId,
328 pub kind: NodeKind,
330 pub span: Span,
332 pub metadata: NodeMetadata,
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn test_node_id_creation() {
342 let id = NodeId::new(Language::Cpp, "src/main.cpp", "main");
343 assert_eq!(id.language, Language::Cpp);
344 assert_eq!(id.file.as_ref(), "src/main.cpp");
345 assert_eq!(id.qualified_name.as_ref(), "main");
346 }
347
348 #[test]
349 fn test_node_id_display() {
350 let id = NodeId::new(Language::Python, "api.py", "User.authenticate");
351 assert_eq!(id.to_string(), "py:api.py:User.authenticate");
352 }
353
354 #[test]
355 fn test_node_id_hash() {
356 use std::collections::HashSet;
357
358 let id1 = NodeId::new(Language::JavaScript, "api.js", "fetchUsers");
359 let id2 = NodeId::new(Language::JavaScript, "api.js", "fetchUsers");
360 let id3 = NodeId::new(Language::JavaScript, "api.js", "createUser");
361
362 let mut set = HashSet::new();
363 set.insert(id1.clone());
364 set.insert(id2.clone());
365 set.insert(id3.clone());
366
367 assert_eq!(set.len(), 2); }
369
370 #[test]
371 fn test_node_id_clone_cheap() {
372 let id1 = NodeId::new(Language::Cpp, "src/utils.cpp", "std::vector::push_back");
373 let id2 = id1.clone();
374
375 assert_eq!(Arc::as_ptr(&id1.file), Arc::as_ptr(&id2.file));
377 assert_eq!(
378 Arc::as_ptr(&id1.qualified_name),
379 Arc::as_ptr(&id2.qualified_name)
380 );
381 }
382
383 #[test]
384 fn test_symbol_name_extraction() {
385 let id1 = NodeId::new(Language::Cpp, "main.cpp", "std::vector::push_back");
386 assert_eq!(id1.symbol_name(), "push_back");
387
388 let id2 = NodeId::new(Language::Python, "api.py", "User.authenticate");
389 assert_eq!(id2.symbol_name(), "authenticate");
390
391 let id3 = NodeId::new(Language::JavaScript, "api.js", "fetchUsers");
392 assert_eq!(id3.symbol_name(), "fetchUsers");
393 }
394
395 #[test]
396 fn test_span_creation() {
397 let span = Span::new(Position::new(10, 0), Position::new(20, 1));
398
399 assert_eq!(span.start.line, 10);
400 assert_eq!(span.end.line, 20);
401 }
402
403 #[test]
404 fn test_language_display() {
405 assert_eq!(Language::Cpp.to_string(), "cpp");
406 assert_eq!(Language::JavaScript.to_string(), "js");
407 assert_eq!(Language::Python.to_string(), "py");
408 assert_eq!(Language::Ruby.to_string(), "ruby");
409 assert_eq!(Language::Php.to_string(), "php");
410 assert_eq!(Language::Swift.to_string(), "swift");
411 assert_eq!(Language::Kotlin.to_string(), "kotlin");
412 assert_eq!(Language::Scala.to_string(), "scala");
413 assert_eq!(Language::Http.to_string(), "http");
414 }
415}