use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream { let type_ident = format_ident!("{}", spec.build()); let child_adders = spec .children() .map(|child| match child { StructChild::SkipChild(_) => None, StructChild::VecChild(vec_child) => match vec_child.build() { VecChildBuild::String(_) => None, VecChildBuild::Node(_) => { let child_ident = format_ident!("{}", vec_child.name()); let children_stream = quote! { for child in self.#child_ident() { children.push(child as &dyn AstNode); } }; Some(children_stream) } }, StructChild::MemberChild(member_child) => match member_child.build() { MemberChildBuild::Node(_) => { let child_ident = format_ident!("{}", member_child.name()); if member_child.optional() { Some(quote! { if let Some(#child_ident) = self.#child_ident() { children.push(#child_ident as &dyn AstNode); } }) } else { Some(quote! { children.push(self.#child_ident() as &dyn AstNode) }) } } MemberChildBuild::Boolean(_) => None, }, StructChild::Special(_) => None, }) .filter(Option::is_some) .map(Option::unwrap) .collect::>(); quote! { impl<'a> AstNode<'a> for #type_ident { fn children(&'a self) -> Vec<&'a dyn AstNode<'a>> { let mut children = vec![]; #(#child_adders;)* children } fn as_node_ref(&'a self) -> AstNodeRef<'a> { AstNodeRef::#type_ident(&self) } } } }