codeprism_lang_js/
types.rs

1//! Types for JavaScript/TypeScript parser
2//!
3//! These types mirror the ones in codeprism_core::ast but are defined here to avoid
4//! circular dependencies. The parser returns these types which are then
5//! converted to codeprism types by the caller.
6
7use blake3::Hasher;
8use serde::{Deserialize, Serialize};
9use std::path::{Path, PathBuf};
10
11/// Unique identifier for AST nodes
12#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct NodeId([u8; 16]);
14
15impl NodeId {
16    /// Create a new NodeId from components
17    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    /// Get the ID as a hex string
32    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/// Types of nodes in the Universal AST
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum NodeKind {
47    /// A module or file
48    Module,
49    /// A class definition
50    Class,
51    /// A function definition
52    Function,
53    /// A method definition
54    Method,
55    /// A function/method parameter
56    Parameter,
57    /// A variable declaration
58    Variable,
59    /// A function/method call
60    Call,
61    /// An import statement
62    Import,
63    /// A literal value
64    Literal,
65    /// An HTTP route definition
66    Route,
67    /// A SQL query
68    SqlQuery,
69    /// An event emission
70    Event,
71    /// Unknown node type
72    Unknown,
73}
74
75/// Types of edges between nodes
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
78pub enum EdgeKind {
79    /// Function/method call
80    Calls,
81    /// Variable/field read
82    Reads,
83    /// Variable/field write
84    Writes,
85    /// Module import
86    Imports,
87    /// Event emission
88    Emits,
89    /// HTTP route mapping
90    RoutesTo,
91    /// Exception raising
92    Raises,
93    /// Type inheritance
94    Extends,
95    /// Interface implementation
96    Implements,
97}
98
99/// Source code location
100#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
101pub struct Span {
102    /// Starting byte offset
103    pub start_byte: usize,
104    /// Ending byte offset (exclusive)
105    pub end_byte: usize,
106    /// Starting line (1-indexed)
107    pub start_line: usize,
108    /// Ending line (1-indexed)
109    pub end_line: usize,
110    /// Starting column (1-indexed)
111    pub start_column: usize,
112    /// Ending column (1-indexed)
113    pub end_column: usize,
114}
115
116impl Span {
117    /// Create a new span
118    pub fn new(
119        start_byte: usize,
120        end_byte: usize,
121        start_line: usize,
122        end_line: usize,
123        start_column: usize,
124        end_column: usize,
125    ) -> Self {
126        Self {
127            start_byte,
128            end_byte,
129            start_line,
130            end_line,
131            start_column,
132            end_column,
133        }
134    }
135
136    /// Create a span from tree-sitter node
137    pub fn from_node(node: &tree_sitter::Node) -> Self {
138        let start_pos = node.start_position();
139        let end_pos = node.end_position();
140
141        Self {
142            start_byte: node.start_byte(),
143            end_byte: node.end_byte(),
144            start_line: start_pos.row + 1, // tree-sitter uses 0-indexed
145            end_line: end_pos.row + 1,
146            start_column: start_pos.column + 1,
147            end_column: end_pos.column + 1,
148        }
149    }
150}
151
152/// Programming language
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
154#[serde(rename_all = "lowercase")]
155pub enum Language {
156    /// JavaScript
157    JavaScript,
158    /// TypeScript
159    TypeScript,
160}
161
162/// A node in the Universal AST
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct Node {
165    /// Unique identifier
166    pub id: NodeId,
167    /// Node type
168    pub kind: NodeKind,
169    /// Node name (e.g., function name)
170    pub name: String,
171    /// Programming language
172    pub lang: Language,
173    /// Source file path
174    pub file: PathBuf,
175    /// Source location
176    pub span: Span,
177    /// Optional type signature
178    pub signature: Option<String>,
179    /// Additional metadata
180    pub metadata: serde_json::Value,
181}
182
183impl Node {
184    /// Create a new node
185    pub fn new(
186        repo_id: &str,
187        kind: NodeKind,
188        name: String,
189        lang: Language,
190        file: PathBuf,
191        span: Span,
192    ) -> Self {
193        let id = NodeId::new(repo_id, &file, &span, &kind);
194        Self {
195            id,
196            kind,
197            name,
198            lang,
199            file,
200            span,
201            signature: None,
202            metadata: serde_json::Value::Null,
203        }
204    }
205}
206
207/// An edge between nodes
208#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
209pub struct Edge {
210    /// Source node ID
211    pub source: NodeId,
212    /// Target node ID
213    pub target: NodeId,
214    /// Edge type
215    pub kind: EdgeKind,
216}
217
218impl Edge {
219    /// Create a new edge
220    pub fn new(source: NodeId, target: NodeId, kind: EdgeKind) -> Self {
221        Self {
222            source,
223            target,
224            kind,
225        }
226    }
227}