1use super::type_mapper::{
2 AlternationEnumDef, ChildrenDef, FieldDef, FieldType, LeafStructDef, StructDef,
3 SupertypeEnumDef, TypeDecision, TypeReference, VariantDef,
4};
5use proc_macro2::TokenStream;
6use quote::{format_ident, quote};
7
8pub fn emit(decisions: &[TypeDecision]) -> TokenStream {
10 let mut tokens = TokenStream::new();
11
12 let mut no_lifetime_types: std::collections::HashSet<String> = decisions
15 .iter()
16 .filter_map(|d| {
17 if let TypeDecision::SupertypeEnum(def) = d {
18 if !def.variants.iter().any(|v| v.named) {
19 return Some(def.type_name.to_string());
20 }
21 }
22 None
23 })
24 .collect();
25
26 loop {
29 let mut changed = false;
30 for decision in decisions {
31 if let TypeDecision::Struct(def) = decision {
32 let name = def.type_name.to_string();
33 if !no_lifetime_types.contains(&name)
34 && !struct_needs_lifetime(def, &no_lifetime_types)
35 {
36 no_lifetime_types.insert(name);
37 changed = true;
38 }
39 }
40 }
41 if !changed {
42 break;
43 }
44 }
45
46 let mut alternation_enums: Vec<&AlternationEnumDef> = Vec::new();
48 for decision in decisions {
49 if let TypeDecision::Struct(def) = decision {
50 collect_alternation_enums(def, &mut alternation_enums);
51 }
52 }
53
54 for decision in decisions {
55 match decision {
56 TypeDecision::Struct(def) => tokens.extend(emit_struct(def, &no_lifetime_types)),
57 TypeDecision::LeafStruct(def) => tokens.extend(emit_leaf_struct(def)),
58 TypeDecision::SupertypeEnum(def) => {
59 tokens.extend(emit_supertype_enum(def, &no_lifetime_types))
60 }
61 }
62 }
63
64 for alt in &alternation_enums {
65 tokens.extend(emit_alternation_enum(alt, &no_lifetime_types));
66 }
67
68 tokens.extend(emit_any_node(decisions, &no_lifetime_types));
70
71 tokens
72}
73
74fn collect_alternation_enums<'a>(def: &'a StructDef, out: &mut Vec<&'a AlternationEnumDef>) {
75 for field in &def.fields {
76 collect_alternation_from_field_type(&field.field_type, out);
77 }
78 if let Some(children) = &def.children {
79 collect_alternation_from_field_type(&children.field_type, out);
80 }
81}
82
83fn collect_alternation_from_field_type<'a>(
84 ft: &'a FieldType,
85 out: &mut Vec<&'a AlternationEnumDef>,
86) {
87 let type_ref = match ft {
88 FieldType::Direct(tr) | FieldType::Optional(tr) | FieldType::Repeated(tr) => tr,
89 };
90 if let TypeReference::Alternation(alt) = type_ref {
91 out.push(alt);
92 }
93}
94
95fn field_type_needs_lifetime(
97 ft: &FieldType,
98 no_lifetime_types: &std::collections::HashSet<String>,
99) -> bool {
100 let tr = match ft {
101 FieldType::Direct(tr) | FieldType::Optional(tr) | FieldType::Repeated(tr) => tr,
102 };
103 match tr {
104 TypeReference::Named(ident) => !no_lifetime_types.contains(&ident.to_string()),
105 TypeReference::Alternation(alt) => alt.variants.iter().any(|v| v.named),
106 }
107}
108
109fn struct_needs_lifetime(
111 def: &StructDef,
112 no_lifetime_types: &std::collections::HashSet<String>,
113) -> bool {
114 def.fields
115 .iter()
116 .any(|f| field_type_needs_lifetime(&f.field_type, no_lifetime_types))
117 || def
118 .children
119 .as_ref()
120 .is_some_and(|c| field_type_needs_lifetime(&c.field_type, no_lifetime_types))
121}
122
123fn emit_struct(
124 def: &StructDef,
125 no_lifetime_types: &std::collections::HashSet<String>,
126) -> TokenStream {
127 let type_name = &def.type_name;
128 let kind_str = &def.kind;
129 let needs_lifetime = struct_needs_lifetime(def, no_lifetime_types);
130
131 let field_decls: Vec<_> = def
132 .fields
133 .iter()
134 .map(|f| emit_field_decl(f, type_name, no_lifetime_types))
135 .collect();
136 let field_parsers: Vec<_> = def
137 .fields
138 .iter()
139 .map(|f| emit_field_parser(f, type_name, no_lifetime_types))
140 .collect();
141
142 let (children_decl, children_parser) = if let Some(children) = &def.children {
143 (
144 Some(emit_children_decl(children, type_name, no_lifetime_types)),
145 Some(emit_children_parser(children, type_name)),
146 )
147 } else {
148 (None, None)
149 };
150
151 if needs_lifetime {
152 quote! {
153 #[derive(Debug, Clone, PartialEq, Eq)]
154 pub struct #type_name<'tree> {
155 pub span: ::treesitter_types::Span,
156 #(#field_decls)*
157 #children_decl
158 }
159
160 impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
161 #[allow(clippy::match_single_binding, clippy::suspicious_else_formatting)]
162 fn from_node(
163 node: ::treesitter_types::tree_sitter::Node<'tree>,
164 src: &'tree [u8],
165 ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
166 debug_assert_eq!(node.kind(), #kind_str);
167 Ok(Self {
168 span: ::treesitter_types::Span::from(node),
169 #(#field_parsers)*
170 #children_parser
171 })
172 }
173 }
174
175 impl ::treesitter_types::Spanned for #type_name<'_> {
176 fn span(&self) -> ::treesitter_types::Span {
177 self.span
178 }
179 }
180 }
181 } else {
182 let has_fields = !def.fields.is_empty() || def.children.is_some();
185 let src_param = if has_fields {
186 quote! { src: &'tree [u8] }
187 } else {
188 quote! { _src: &'tree [u8] }
189 };
190 quote! {
191 #[derive(Debug, Clone, PartialEq, Eq)]
192 pub struct #type_name {
193 pub span: ::treesitter_types::Span,
194 #(#field_decls)*
195 #children_decl
196 }
197
198 impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name {
199 #[allow(clippy::match_single_binding, clippy::suspicious_else_formatting)]
200 fn from_node(
201 node: ::treesitter_types::tree_sitter::Node<'tree>,
202 #src_param,
203 ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
204 debug_assert_eq!(node.kind(), #kind_str);
205 Ok(Self {
206 span: ::treesitter_types::Span::from(node),
207 #(#field_parsers)*
208 #children_parser
209 })
210 }
211 }
212
213 impl ::treesitter_types::Spanned for #type_name {
214 fn span(&self) -> ::treesitter_types::Span {
215 self.span
216 }
217 }
218 }
219 }
220}
221
222fn emit_field_decl(
223 field: &FieldDef,
224 parent_type: &proc_macro2::Ident,
225 no_lifetime_types: &std::collections::HashSet<String>,
226) -> TokenStream {
227 let name = &field.field_name;
228 let ty = emit_rust_type(&field.field_type, parent_type, no_lifetime_types);
229 quote! { pub #name: #ty, }
230}
231
232fn emit_children_decl(
233 children: &ChildrenDef,
234 parent_type: &proc_macro2::Ident,
235 no_lifetime_types: &std::collections::HashSet<String>,
236) -> TokenStream {
237 let ty = emit_rust_type(&children.field_type, parent_type, no_lifetime_types);
238 quote! { pub children: #ty, }
239}
240
241fn is_self_referential(tr: &TypeReference, parent_type: &proc_macro2::Ident) -> bool {
243 match tr {
244 TypeReference::Named(ident) => ident == parent_type,
245 TypeReference::Alternation(_) => false,
246 }
247}
248
249fn emit_rust_type(
250 ft: &FieldType,
251 parent_type: &proc_macro2::Ident,
252 no_lifetime_types: &std::collections::HashSet<String>,
253) -> TokenStream {
254 match ft {
255 FieldType::Direct(tr) => {
256 let inner = emit_type_reference(tr, no_lifetime_types);
257 if is_self_referential(tr, parent_type) {
258 quote! { ::std::boxed::Box<#inner> }
259 } else {
260 quote! { #inner }
261 }
262 }
263 FieldType::Optional(tr) => {
264 let inner = emit_type_reference(tr, no_lifetime_types);
265 if is_self_referential(tr, parent_type) {
266 quote! { ::core::option::Option<::std::boxed::Box<#inner>> }
267 } else {
268 quote! { ::core::option::Option<#inner> }
269 }
270 }
271 FieldType::Repeated(tr) => {
272 let inner = emit_type_reference(tr, no_lifetime_types);
273 quote! { ::std::vec::Vec<#inner> }
274 }
275 }
276}
277
278fn emit_type_reference(
279 tr: &TypeReference,
280 no_lifetime_types: &std::collections::HashSet<String>,
281) -> TokenStream {
282 match tr {
283 TypeReference::Named(ident) => {
284 if no_lifetime_types.contains(&ident.to_string()) {
285 quote! { #ident }
286 } else {
287 quote! { #ident<'tree> }
288 }
289 }
290 TypeReference::Alternation(alt) => {
291 let name = &alt.type_name;
292 let has_named = alt.variants.iter().any(|v| v.named);
293 if has_named {
294 quote! { #name<'tree> }
295 } else {
296 quote! { #name }
297 }
298 }
299 }
300}
301
302fn emit_field_parser(
303 field: &FieldDef,
304 parent_type: &proc_macro2::Ident,
305 no_lifetime_types: &std::collections::HashSet<String>,
306) -> TokenStream {
307 let _ = no_lifetime_types; let name = &field.field_name;
309 let raw_name = &field.raw_field_name;
310
311 match &field.field_type {
312 FieldType::Direct(type_ref) => {
313 let from_node = emit_from_node_call(type_ref);
314 let self_ref = is_self_referential(type_ref, parent_type);
315 let value_expr = if self_ref {
316 quote! { ::std::boxed::Box::new(#from_node) }
317 } else {
318 from_node
319 };
320 quote! {
321 #name: {
322 let child = node
323 .child_by_field_name(#raw_name)
324 .ok_or_else(|| ::treesitter_types::ParseError::missing_field(#raw_name, node))?;
325 #value_expr
326 },
327 }
328 }
329 FieldType::Optional(type_ref) => {
330 let from_node = emit_from_node_call(type_ref);
331 let self_ref = is_self_referential(type_ref, parent_type);
332 let some_expr = if self_ref {
333 quote! { Some(::std::boxed::Box::new(#from_node)) }
334 } else {
335 quote! { Some(#from_node) }
336 };
337 quote! {
338 #name: match node.child_by_field_name(#raw_name) {
339 Some(child) => #some_expr,
340 None => None,
341 },
342 }
343 }
344 FieldType::Repeated(type_ref) => {
345 let from_node = emit_from_node_call(type_ref);
346 quote! {
347 #name: {
348 let mut cursor = node.walk();
349 let mut items = ::std::vec::Vec::new();
350 for child in node.children_by_field_name(#raw_name, &mut cursor) {
351 items.push(#from_node);
352 }
353 items
354 },
355 }
356 }
357 }
358}
359
360fn emit_non_field_children_collector() -> TokenStream {
366 quote! {
367 #[allow(clippy::suspicious_else_formatting)]
368 let non_field_children = {
369 let mut cursor = node.walk();
370 let mut result = ::std::vec::Vec::new();
371 if cursor.goto_first_child() {
372 loop {
373 if cursor.field_name().is_none() && cursor.node().is_named() && !cursor.node().is_extra() {
374 result.push(cursor.node());
375 }
376 if !cursor.goto_next_sibling() {
377 break;
378 }
379 }
380 }
381 result
382 };
383 }
384}
385
386fn emit_children_parser(children: &ChildrenDef, parent_type: &proc_macro2::Ident) -> TokenStream {
387 let collector = emit_non_field_children_collector();
388 match &children.field_type {
389 FieldType::Direct(type_ref) => {
390 let from_node = emit_from_node_call_named_children(type_ref);
391 let self_ref = is_self_referential(type_ref, parent_type);
392 let value_expr = if self_ref {
393 quote! { ::std::boxed::Box::new(#from_node) }
394 } else {
395 from_node
396 };
397 quote! {
403 children: {
404 #collector
405 let child = if let Some(&c) = non_field_children.first() {
406 c
407 } else {
408 let mut fallback_cursor = node.walk();
409 let mut fallback_child = None;
410 if fallback_cursor.goto_first_child() {
411 loop {
412 if fallback_cursor.field_name().is_none() && !fallback_cursor.node().is_extra() {
413 let candidate = fallback_cursor.node();
414 #[allow(clippy::needless_question_mark)]
415 if (|| -> ::core::result::Result<_, ::treesitter_types::ParseError> {
416 let child = candidate;
417 Ok(#value_expr)
418 })().is_ok() {
419 fallback_child = Some(candidate);
420 break;
421 }
422 }
423 if !fallback_cursor.goto_next_sibling() {
424 break;
425 }
426 }
427 }
428 if fallback_child.is_none() {
433 let mut cursor2 = node.walk();
434 if cursor2.goto_first_child() {
435 loop {
436 if cursor2.node().is_named() && !cursor2.node().is_extra() {
437 let candidate = cursor2.node();
438 #[allow(clippy::needless_question_mark)]
439 if (|| -> ::core::result::Result<_, ::treesitter_types::ParseError> {
440 let child = candidate;
441 Ok(#value_expr)
442 })().is_ok() {
443 fallback_child = Some(candidate);
444 break;
445 }
446 }
447 if !cursor2.goto_next_sibling() {
448 break;
449 }
450 }
451 }
452 }
453 fallback_child.ok_or_else(|| ::treesitter_types::ParseError::missing_field("children", node))?
454 };
455 #value_expr
456 },
457 }
458 }
459 FieldType::Optional(type_ref) => {
460 let from_node = emit_from_node_call_named_children(type_ref);
461 let self_ref = is_self_referential(type_ref, parent_type);
462 let some_expr = if self_ref {
463 quote! { Some(::std::boxed::Box::new(#from_node)) }
464 } else {
465 quote! { Some(#from_node) }
466 };
467 quote! {
468 children: {
469 #collector
470 match non_field_children.first() {
471 Some(&child) => #some_expr,
472 None => None,
473 }
474 },
475 }
476 }
477 FieldType::Repeated(type_ref) => {
478 let from_node = emit_from_node_call_named_children(type_ref);
479 quote! {
480 children: {
481 #collector
482 let mut items = ::std::vec::Vec::new();
483 for child in non_field_children {
484 items.push(#from_node);
485 }
486 items
487 },
488 }
489 }
490 }
491}
492
493fn emit_from_node_call(type_ref: &TypeReference) -> TokenStream {
494 match type_ref {
495 TypeReference::Named(ident) => {
496 quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#ident as ::treesitter_types::FromNode>::from_node(child, src))? }
497 }
498 TypeReference::Alternation(alt) => {
499 let name = &alt.type_name;
500 quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#name as ::treesitter_types::FromNode>::from_node(child, src))? }
501 }
502 }
503}
504
505fn emit_from_node_call_named_children(type_ref: &TypeReference) -> TokenStream {
506 match type_ref {
507 TypeReference::Named(ident) => {
508 quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#ident as ::treesitter_types::FromNode>::from_node(child, src))? }
509 }
510 TypeReference::Alternation(alt) => {
511 let name = &alt.type_name;
512 quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#name as ::treesitter_types::FromNode>::from_node(child, src))? }
513 }
514 }
515}
516
517fn emit_leaf_struct(def: &LeafStructDef) -> TokenStream {
518 let type_name = &def.type_name;
519 let kind_str = &def.kind;
520
521 quote! {
522 #[derive(Debug, Clone, PartialEq, Eq)]
523 pub struct #type_name<'tree> {
524 pub span: ::treesitter_types::Span,
525 text: &'tree str,
526 }
527
528 impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
529 fn from_node(
530 node: ::treesitter_types::tree_sitter::Node<'tree>,
531 src: &'tree [u8],
532 ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
533 debug_assert_eq!(node.kind(), #kind_str);
534 Ok(Self {
535 span: ::treesitter_types::Span::from(node),
536 text: node.utf8_text(src)?,
537 })
538 }
539 }
540
541 impl<'tree> ::treesitter_types::LeafNode<'tree> for #type_name<'tree> {
542 fn text(&self) -> &'tree str {
543 self.text
544 }
545 }
546
547 impl ::treesitter_types::Spanned for #type_name<'_> {
548 fn span(&self) -> ::treesitter_types::Span {
549 self.span
550 }
551 }
552 }
553}
554
555fn deduplicate_variants(variants: &[VariantDef]) -> Vec<VariantDef> {
559 let mut seen: std::collections::HashMap<(String, bool), usize> =
562 std::collections::HashMap::new();
563 let mut result: Vec<VariantDef> = Vec::new();
564 let mut used_names: std::collections::HashSet<String> = std::collections::HashSet::new();
565 for v in variants {
566 let key = (v.variant_name.to_string(), v.named);
567 if let Some(&idx) = seen.get(&key) {
568 result[idx].extra_kinds.push(v.kind.clone());
570 } else {
571 let mut v = v.clone();
572 let name_str = v.variant_name.to_string();
575 if used_names.contains(&name_str) && !v.named {
576 v.variant_name = format_ident!("{}Kw", name_str);
577 }
578 let actual_name = v.variant_name.to_string();
579 seen.insert(key, result.len());
580 used_names.insert(actual_name);
581 used_names.insert(name_str);
582 result.push(v);
583 }
584 }
585 result
586}
587
588fn emit_alternation_enum(
589 def: &AlternationEnumDef,
590 no_lifetime_types: &std::collections::HashSet<String>,
591) -> TokenStream {
592 emit_enum_common(&def.type_name, &def.variants, no_lifetime_types)
593}
594
595fn emit_supertype_enum(
596 def: &SupertypeEnumDef,
597 no_lifetime_types: &std::collections::HashSet<String>,
598) -> TokenStream {
599 emit_enum_common(&def.type_name, &def.variants, no_lifetime_types)
600}
601
602fn emit_enum_common(
603 type_name: &proc_macro2::Ident,
604 variants: &[VariantDef],
605 no_lifetime_types: &std::collections::HashSet<String>,
606) -> TokenStream {
607 let variants = deduplicate_variants(variants);
611 let has_named = variants.iter().any(|v| v.named);
612 let variant_decls: Vec<_> = variants
613 .iter()
614 .map(|v| emit_enum_variant_decl(v, no_lifetime_types))
615 .collect();
616
617 let (supertype_variants, concrete_variants): (Vec<_>, Vec<_>) =
621 variants.iter().partition(|v| v.is_supertype);
622
623 let concrete_arms: Vec<_> = concrete_variants
624 .iter()
625 .map(|v| emit_enum_match_arm(v))
626 .collect();
627 let fallback_branch = if supertype_variants.is_empty() {
630 quote! { other => Err(::treesitter_types::ParseError::unexpected_kind(other, node)) }
631 } else {
632 let mut chain = quote! {
633 Err(::treesitter_types::ParseError::unexpected_kind(_other, node))
634 };
635 for v in supertype_variants.iter().rev() {
636 let name = &v.variant_name;
637 let type_name = format_ident!("{}", name);
638 chain = quote! {
639 if let Ok(v) = ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src)) {
640 Ok(Self::#name(::std::boxed::Box::new(v)))
641 } else {
642 #chain
643 }
644 };
645 }
646 quote! { _other => { #chain } }
647 };
648
649 let from_node_body = if concrete_arms.is_empty() {
651 let mut chain = quote! {
653 Err(::treesitter_types::ParseError::unexpected_kind(node.kind(), node))
654 };
655 for v in supertype_variants.iter().rev() {
656 let name = &v.variant_name;
657 let type_name = format_ident!("{}", name);
658 chain = quote! {
659 if let Ok(v) = ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src)) {
660 Ok(Self::#name(::std::boxed::Box::new(v)))
661 } else {
662 #chain
663 }
664 };
665 }
666 chain
667 } else {
668 quote! {
669 match node.kind() {
670 #(#concrete_arms)*
671 #fallback_branch
672 }
673 }
674 };
675
676 let spanned_arms: Vec<_> = variants.iter().map(emit_enum_spanned_arm).collect();
677
678 if has_named {
679 quote! {
680 #[derive(Debug, Clone, PartialEq, Eq)]
681 pub enum #type_name<'tree> {
682 #(#variant_decls)*
683 }
684
685 impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
686 #[allow(clippy::collapsible_else_if)]
687 fn from_node(
688 node: ::treesitter_types::tree_sitter::Node<'tree>,
689 src: &'tree [u8],
690 ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
691 #from_node_body
692 }
693 }
694
695 impl ::treesitter_types::Spanned for #type_name<'_> {
696 fn span(&self) -> ::treesitter_types::Span {
697 match self {
698 #(#spanned_arms)*
699 }
700 }
701 }
702 }
703 } else {
704 let has_supertypes = !supertype_variants.is_empty();
707 let src_param = if has_supertypes {
708 quote! { src: &'tree [u8] }
709 } else {
710 quote! { _src: &'tree [u8] }
711 };
712 quote! {
713 #[derive(Debug, Clone, PartialEq, Eq)]
714 pub enum #type_name {
715 #(#variant_decls)*
716 }
717
718 impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name {
719 #[allow(clippy::collapsible_else_if)]
720 fn from_node(
721 node: ::treesitter_types::tree_sitter::Node<'tree>,
722 #src_param,
723 ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
724 #from_node_body
725 }
726 }
727
728 impl ::treesitter_types::Spanned for #type_name {
729 fn span(&self) -> ::treesitter_types::Span {
730 match self {
731 #(#spanned_arms)*
732 }
733 }
734 }
735 }
736 }
737}
738
739fn emit_enum_variant_decl(
740 variant: &VariantDef,
741 no_lifetime_types: &std::collections::HashSet<String>,
742) -> TokenStream {
743 let name = &variant.variant_name;
744 if variant.named {
745 let type_name = format_ident!("{}", name);
746 if no_lifetime_types.contains(&type_name.to_string()) {
747 quote! { #name(::std::boxed::Box<#type_name>), }
748 } else {
749 quote! { #name(::std::boxed::Box<#type_name<'tree>>), }
750 }
751 } else {
752 quote! { #name(::treesitter_types::Span), }
754 }
755}
756
757fn emit_enum_match_arm(variant: &VariantDef) -> TokenStream {
758 let kind_str = &variant.kind;
759 let name = &variant.variant_name;
760 let extra = &variant.extra_kinds;
761 if variant.named {
762 let type_name = format_ident!("{}", name);
763 quote! {
764 #kind_str #(| #extra)* => Ok(Self::#name(
765 ::std::boxed::Box::new(::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src))?)
766 )),
767 }
768 } else {
769 quote! {
770 #kind_str #(| #extra)* => Ok(Self::#name(::treesitter_types::Span::from(node))),
771 }
772 }
773}
774
775fn emit_enum_spanned_arm(variant: &VariantDef) -> TokenStream {
776 let name = &variant.variant_name;
777 if variant.named {
778 quote! { Self::#name(inner) => inner.span(), }
779 } else {
780 quote! { Self::#name(span) => *span, }
781 }
782}
783
784fn emit_any_node(
785 decisions: &[TypeDecision],
786 no_lifetime_types: &std::collections::HashSet<String>,
787) -> TokenStream {
788 let mut variant_decls = Vec::new();
789 let mut match_arms = Vec::new();
790 let mut spanned_arms = Vec::new();
791
792 for decision in decisions {
793 let (type_name, kind_str) = match decision {
794 TypeDecision::Struct(def) => (&def.type_name, &def.kind),
795 TypeDecision::LeafStruct(def) => (&def.type_name, &def.kind),
796 TypeDecision::SupertypeEnum(def) => (&def.type_name, &def.kind),
797 };
798 let needs_lifetime = !no_lifetime_types.contains(&type_name.to_string());
799
800 if needs_lifetime {
801 variant_decls.push(quote! {
802 #type_name(#type_name<'tree>),
803 });
804 } else {
805 variant_decls.push(quote! {
806 #type_name(#type_name),
807 });
808 }
809
810 match_arms.push(quote! {
811 #kind_str => ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src))
812 .map(Self::#type_name)
813 .unwrap_or(Self::Unknown(node)),
814 });
815
816 spanned_arms.push(quote! {
817 Self::#type_name(inner) => inner.span(),
818 });
819 }
820
821 quote! {
822 #[derive(Debug, Clone, PartialEq, Eq)]
823 pub enum AnyNode<'tree> {
824 #(#variant_decls)*
825 Unknown(::treesitter_types::tree_sitter::Node<'tree>),
826 }
827
828 impl<'tree> AnyNode<'tree> {
829 pub fn from_node(node: ::treesitter_types::tree_sitter::Node<'tree>, src: &'tree [u8]) -> Self {
830 match node.kind() {
831 #(#match_arms)*
832 _ => Self::Unknown(node),
833 }
834 }
835 }
836
837 impl ::treesitter_types::Spanned for AnyNode<'_> {
838 fn span(&self) -> ::treesitter_types::Span {
839 match self {
840 #(#spanned_arms)*
841 Self::Unknown(node) => ::treesitter_types::Span::from(*node),
842 }
843 }
844 }
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851 use crate::codegen::grammar_ir::parse_node_types;
852 use crate::codegen::type_mapper::map_types;
853
854 fn generate_and_stringify(json: &str) -> String {
855 let nodes = parse_node_types(json).unwrap();
856 let decisions = map_types(&nodes);
857 let tokens = emit(&decisions);
858 tokens.to_string()
859 }
860
861 #[test]
862 fn test_emit_leaf_struct() {
863 let code = generate_and_stringify(r#"[{"type": "identifier", "named": true}]"#);
864 assert!(code.contains("pub struct Identifier"));
865 assert!(code
866 .contains("impl < 'tree > :: treesitter_types :: LeafNode < 'tree > for Identifier"));
867 assert!(code.contains("fn text"));
868 }
869
870 #[test]
871 fn test_emit_struct_with_required_field() {
872 let json = r#"[
873 {"type": "identifier", "named": true},
874 {
875 "type": "import_spec",
876 "named": true,
877 "fields": {
878 "path": {
879 "multiple": false,
880 "required": true,
881 "types": [{"type": "identifier", "named": true}]
882 }
883 }
884 }
885 ]"#;
886 let code = generate_and_stringify(json);
887 assert!(code.contains("pub struct ImportSpec"));
888 assert!(code.contains("pub path : Identifier < 'tree >"));
889 assert!(code.contains("child_by_field_name (\"path\")"));
890 }
891
892 #[test]
893 fn test_emit_struct_with_optional_field() {
894 let json = r#"[
895 {"type": "identifier", "named": true},
896 {
897 "type": "import_spec",
898 "named": true,
899 "fields": {
900 "name": {
901 "multiple": false,
902 "required": false,
903 "types": [{"type": "identifier", "named": true}]
904 }
905 }
906 }
907 ]"#;
908 let code = generate_and_stringify(json);
909 assert!(code.contains("Option < Identifier < 'tree > >"));
910 }
911
912 #[test]
913 fn test_emit_alternation_enum() {
914 let json = r#"[
915 {"type": "identifier", "named": true},
916 {
917 "type": "import_spec",
918 "named": true,
919 "fields": {
920 "name": {
921 "multiple": false,
922 "required": false,
923 "types": [
924 {"type": ".", "named": false},
925 {"type": "identifier", "named": true}
926 ]
927 }
928 }
929 }
930 ]"#;
931 let code = generate_and_stringify(json);
932 assert!(code.contains("pub enum ImportSpecName"));
933 assert!(code.contains("Dot"));
934 assert!(code.contains("Identifier (Identifier < 'tree >)"));
935 }
936
937 #[test]
938 fn test_emit_supertype_enum() {
939 let json = r#"[
940 {"type": "identifier", "named": true},
941 {"type": "binary_expression", "named": true},
942 {
943 "type": "_expression",
944 "named": true,
945 "subtypes": [
946 {"type": "binary_expression", "named": true},
947 {"type": "identifier", "named": true}
948 ]
949 }
950 ]"#;
951 let code = generate_and_stringify(json);
952 assert!(code.contains("pub enum Expression"));
953 assert!(code.contains("BinaryExpression (BinaryExpression < 'tree >)"));
954 assert!(code.contains("Identifier (Identifier < 'tree >)"));
955 }
956
957 #[test]
958 fn test_emit_any_node() {
959 let json = r#"[
960 {"type": "identifier", "named": true},
961 {"type": ".", "named": false}
962 ]"#;
963 let code = generate_and_stringify(json);
964 assert!(code.contains("pub enum AnyNode"));
965 assert!(code.contains("Identifier (Identifier < 'tree >)"));
966 assert!(code.contains("Unknown (:: treesitter_types :: tree_sitter :: Node < 'tree >)"));
967 }
968}