1use serde::{Deserialize, Serialize};
8use std::hash::{Hash, Hasher};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[serde(rename_all = "snake_case")]
17pub enum NodeKind {
18 Function,
20 Method,
22 Class,
24 Interface,
26 Struct,
28 Enum,
30 Variable,
32 Constant,
34 TypeAlias,
36 Module,
38 Import,
40 Export,
42}
43
44impl std::fmt::Display for NodeKind {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 let s = match self {
47 Self::Function => "function",
48 Self::Method => "method",
49 Self::Class => "class",
50 Self::Interface => "interface",
51 Self::Struct => "struct",
52 Self::Enum => "enum",
53 Self::Variable => "variable",
54 Self::Constant => "constant",
55 Self::TypeAlias => "type_alias",
56 Self::Module => "module",
57 Self::Import => "import",
58 Self::Export => "export",
59 };
60 write!(f, "{}", s)
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
66#[serde(rename_all = "snake_case")]
67pub enum Visibility {
68 #[default]
69 Private,
70 Public,
71 Protected,
72 Internal,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct CodeNode {
82 pub id: String,
84
85 pub name: String,
87
88 pub qualified_name: String,
90
91 pub kind: NodeKind,
93
94 pub file: String,
96
97 pub line_start: u32,
99
100 pub line_end: u32,
102
103 pub column: u32,
105
106 pub signature: Option<String>,
108
109 pub visibility: Visibility,
111
112 pub is_async: bool,
114
115 pub is_static: bool,
117
118 pub is_exported: bool,
120
121 pub docstring: Option<String>,
123
124 pub byte_start: u32,
126 pub byte_end: u32,
127
128 pub references: Vec<String>,
131}
132
133impl CodeNode {
134 pub fn compute_id(file: &str, qualified_name: &str, kind: NodeKind) -> String {
139 use std::collections::hash_map::DefaultHasher;
140
141 let mut hasher = DefaultHasher::new();
142 file.hash(&mut hasher);
143 qualified_name.hash(&mut hasher);
144 kind.hash(&mut hasher);
145
146 format!("{:016x}", hasher.finish())
147 }
148
149 pub fn new(
151 name: impl Into<String>,
152 qualified_name: impl Into<String>,
153 kind: NodeKind,
154 file: impl Into<String>,
155 ) -> Self {
156 let name = name.into();
157 let qualified_name = qualified_name.into();
158 let file = file.into();
159 let id = Self::compute_id(&file, &qualified_name, kind);
160
161 Self {
162 id,
163 name,
164 qualified_name,
165 kind,
166 file,
167 line_start: 0,
168 line_end: 0,
169 column: 0,
170 signature: None,
171 visibility: Visibility::default(),
172 is_async: false,
173 is_static: false,
174 is_exported: false,
175 docstring: None,
176 byte_start: 0,
177 byte_end: 0,
178 references: Vec::new(),
179 }
180 }
181
182 pub fn with_lines(mut self, start: u32, end: u32) -> Self {
184 self.line_start = start;
185 self.line_end = end;
186 self
187 }
188
189 pub fn with_bytes(mut self, start: u32, end: u32) -> Self {
191 self.byte_start = start;
192 self.byte_end = end;
193 self
194 }
195
196 pub fn with_column(mut self, column: u32) -> Self {
198 self.column = column;
199 self
200 }
201
202 pub fn with_signature(mut self, sig: impl Into<String>) -> Self {
204 self.signature = Some(sig.into());
205 self
206 }
207
208 pub fn with_visibility(mut self, vis: Visibility) -> Self {
210 self.visibility = vis;
211 self
212 }
213
214 pub fn as_async(mut self) -> Self {
216 self.is_async = true;
217 self
218 }
219
220 pub fn as_static(mut self) -> Self {
222 self.is_static = true;
223 self
224 }
225
226 pub fn as_exported(mut self) -> Self {
228 self.is_exported = true;
229 self
230 }
231
232 pub fn with_references(mut self, refs: Vec<String>) -> Self {
234 self.references = refs;
235 self
236 }
237}
238
239impl PartialEq for CodeNode {
240 fn eq(&self, other: &Self) -> bool {
241 self.id == other.id
242 }
243}
244
245impl Eq for CodeNode {}
246
247impl Hash for CodeNode {
248 fn hash<H: Hasher>(&self, state: &mut H) {
249 self.id.hash(state);
250 }
251}