1#![allow(clippy::enum_glob_use, clippy::if_not_else, clippy::wildcard_imports)]
8
9use serde::ser::{SerializeStruct, Serializer};
10use serde::{Deserialize, Serialize};
11
12use crate::*;
13
14pub type Span = Option<(usize, usize, usize, usize)>;
22
23#[derive(Debug, Deserialize, Serialize)]
25pub struct AstPayload {
26 pub id: String,
28 pub file_name: String,
30 pub code: String,
32 pub comment: bool,
34 pub span: bool,
37}
38
39#[derive(Debug, Serialize)]
41pub struct AstResponse {
42 pub id: String,
44 pub root: Option<AstNode>,
48}
49
50#[derive(Debug)]
52pub struct AstNode {
53 pub r#type: &'static str,
55 pub value: String,
57 pub span: Span,
59 pub field_name: Option<&'static str>,
67 pub children: Vec<AstNode>,
69}
70
71impl Serialize for AstNode {
72 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73 where
74 S: Serializer,
75 {
76 let mut st = serializer.serialize_struct("Node", 5)?;
77 st.serialize_field("Type", &self.r#type)?;
78 st.serialize_field("TextValue", &self.value)?;
79 st.serialize_field("Span", &self.span)?;
80 st.serialize_field("FieldName", &self.field_name)?;
81 st.serialize_field("Children", &self.children)?;
82 st.end()
83 }
84}
85
86impl AstNode {
87 #[must_use]
92 pub fn new(r#type: &'static str, value: String, span: Span, children: Vec<AstNode>) -> Self {
93 Self::with_field_name(r#type, value, span, None, children)
94 }
95
96 #[must_use]
100 pub fn with_field_name(
101 r#type: &'static str,
102 value: String,
103 span: Span,
104 field_name: Option<&'static str>,
105 children: Vec<AstNode>,
106 ) -> Self {
107 Self {
108 r#type,
109 value,
110 span,
111 field_name,
112 children,
113 }
114 }
115}
116
117fn build<T: ParserTrait>(parser: &T, span: bool, comment: bool) -> Option<AstNode> {
118 struct Frame<'a> {
126 node: crate::Node<'a>,
127 field: Option<&'static str>,
128 children: Vec<AstNode>,
129 next_child_index: usize,
130 }
131
132 let code = parser.get_code();
133 let root = parser.get_root();
134 let mut stack: Vec<Frame<'_>> = vec![Frame {
135 node: root,
136 field: None,
137 children: Vec::with_capacity(root.child_count()),
138 next_child_index: 0,
139 }];
140
141 loop {
142 let frame = stack
143 .last_mut()
144 .expect("stack invariant: loop only runs while stack is non-empty");
145 let child_count = frame.node.child_count();
146 if frame.next_child_index < child_count {
147 let idx = frame.next_child_index;
148 frame.next_child_index += 1;
149 let child = frame
154 .node
155 .child(idx)
156 .expect("stack invariant: idx < child_count so the child exists");
157 let field = frame.node.field_name_for_child(
158 u32::try_from(idx).expect("invariant: tree-sitter caps child indices at u32::MAX"),
159 );
160 stack.push(Frame {
161 node: child,
162 field,
163 children: Vec::with_capacity(child.child_count()),
164 next_child_index: 0,
165 });
166 } else {
167 let frame = stack
168 .pop()
169 .expect("stack invariant: just observed non-empty via last_mut()");
170 let node = T::Checker::get_ast_node(
171 &frame.node,
172 code,
173 span,
174 comment,
175 frame.field,
176 frame.children,
177 );
178 match (node, stack.last_mut()) {
179 (Some(ast), Some(parent)) => parent.children.push(ast),
180 (Some(ast), None) => return Some(ast),
181 (None, None) => return None,
182 (None, Some(_)) => {}
183 }
184 }
185 }
186}
187
188pub struct AstCallback {
190 _guard: (),
191}
192
193#[derive(Debug)]
195pub struct AstCfg {
196 pub id: String,
198 pub comment: bool,
200 pub span: bool,
203}
204
205impl Callback for AstCallback {
206 type Res = AstResponse;
207 type Cfg = AstCfg;
208
209 fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
210 AstResponse {
211 id: cfg.id,
212 root: build(parser, cfg.span, cfg.comment),
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use std::path::PathBuf;
220
221 use super::*;
222
223 fn build_ast<P: ParserTrait>(code: &[u8], filename: &str) -> AstNode {
224 let path = PathBuf::from(filename);
225 let parser = P::new(code.to_vec(), &path, None);
226 let cfg = AstCfg {
227 id: String::new(),
228 comment: false,
229 span: false,
230 };
231 AstCallback::call(cfg, &parser)
232 .root
233 .expect("parser should produce a root AST node")
234 }
235
236 fn find_first<'a>(node: &'a AstNode, kind: &str) -> Option<&'a AstNode> {
237 if node.r#type == kind {
238 return Some(node);
239 }
240 node.children.iter().find_map(|c| find_first(c, kind))
241 }
242
243 fn find_child<'a>(parent: &'a AstNode, field: &str) -> Option<&'a AstNode> {
244 parent.children.iter().find(|c| c.field_name == Some(field))
245 }
246
247 #[test]
248 fn root_has_no_field_name() {
249 let root = build_ast::<crate::RustParser>(b"fn main() {}", "test.rs");
250 assert_eq!(root.field_name, None);
251 }
252
253 #[test]
254 fn rust_assignment_carries_left_and_right_field_names() {
255 let root =
260 build_ast::<crate::RustParser>(b"fn f() { let mut a = 0; a = a + 1; }", "test.rs");
261 let assign = find_first(&root, "assignment_expression")
262 .expect("expected an assignment_expression node");
263 let left = find_child(assign, "left").expect("expected a `left` child");
264 let right = find_child(assign, "right").expect("expected a `right` child");
265 assert_eq!(left.field_name, Some("left"));
266 assert_eq!(right.field_name, Some("right"));
267 assert!(
269 assign
270 .children
271 .iter()
272 .any(|c| c.r#type == "=" && c.field_name.is_none()),
273 "expected the `=` token child to carry no field name; got {:?}",
274 assign
275 .children
276 .iter()
277 .map(|c| (c.r#type, c.field_name))
278 .collect::<Vec<_>>(),
279 );
280 }
281
282 #[test]
283 fn rust_function_carries_name_and_body_field_names() {
284 let root =
289 build_ast::<crate::RustParser>(b"fn greet(name: &str) -> &str { name }", "test.rs");
290 let func = find_first(&root, "function_item").expect("expected a function_item node");
291 let name_child = find_child(func, "name").expect("function_item should have a name child");
292 assert_eq!(name_child.field_name, Some("name"));
293 assert_eq!(name_child.r#type, "identifier");
294 let params_child =
295 find_child(func, "parameters").expect("function_item should have a parameters child");
296 assert_eq!(params_child.field_name, Some("parameters"));
297 assert_eq!(params_child.r#type, "parameters");
298 let body_child = find_child(func, "body").expect("function_item should have a body child");
299 assert_eq!(body_child.field_name, Some("body"));
300 assert_eq!(body_child.r#type, "block");
301 }
302
303 #[test]
304 fn cpp_assignment_carries_left_and_right_field_names() {
305 let root =
308 build_ast::<crate::CppParser>(b"int main(){ int x = 0; x = x + 1; }", "test.cpp");
309 let assign = find_first(&root, "assignment_expression")
310 .expect("expected an assignment_expression node");
311 assert_eq!(
312 find_child(assign, "left").map(|n| n.r#type),
313 Some("identifier")
314 );
315 assert_eq!(
316 find_child(assign, "right").map(|n| n.r#type),
317 Some("binary_expression")
318 );
319 }
320
321 #[test]
322 fn serialized_json_includes_field_name_key() {
323 let root = build_ast::<crate::RustParser>(b"fn f(){ let a = 1; }", "test.rs");
328 let json = serde_json::to_string(&root).expect("serialize");
329 assert!(
330 json.contains("\"FieldName\""),
331 "FieldName missing from JSON: {json}"
332 );
333 assert!(
336 json.contains("\"FieldName\":\"pattern\""),
337 "expected pattern field name; got {json}"
338 );
339 assert!(
340 json.contains("\"FieldName\":\"value\""),
341 "expected value field name; got {json}"
342 );
343 }
344}