zyn_core/ast/at/
element_node.rs1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4use proc_macro2::TokenTree;
5
6use quote::ToTokens;
7use quote::quote;
8
9use syn::Token;
10use syn::parse::Parse;
11use syn::parse::ParseStream;
12
13use crate::template::Template;
14
15use crate::Expand;
16use crate::pascal;
17
18pub struct ElementNode {
28 pub span: Span,
30 pub name: TokenStream,
32 pub props: Vec<(syn::Ident, TokenStream)>,
34 pub children: Option<Box<Template>>,
36}
37
38impl ElementNode {
39 pub fn span(&self) -> Span {
40 self.span
41 }
42
43 pub fn to_display_stream(&self, injections: &[(String, TokenStream)]) -> TokenStream {
44 let name = pascal!(self.name => token_stream);
45 let prop_names: Vec<&syn::Ident> = self.props.iter().map(|(n, _)| n).collect();
46 let prop_values: Vec<&TokenStream> = self.props.iter().map(|(_, v)| v).collect();
47
48 if let Some(children) = &self.children {
49 let children_display = children.to_display_stream(injections);
50 quote! { #name { #(#prop_names: #prop_values,)* children: { #children_display } } }
51 } else {
52 quote! { #name { #(#prop_names: #prop_values,)* } }
53 }
54 }
55
56 pub fn parse_with_ident(input: ParseStream, first_ident: syn::Ident) -> syn::Result<Self> {
57 let span = first_ident.span();
58
59 let mut name = TokenStream::new();
60 first_ident.to_tokens(&mut name);
61
62 while input.peek(Token![::]) {
63 let colons: Token![::] = input.parse()?;
64 colons.to_tokens(&mut name);
65 let segment: syn::Ident = input.parse()?;
66 segment.to_tokens(&mut name);
67 }
68
69 parse_props_and_children(input, span, name)
70 }
71}
72
73impl Expand for ElementNode {
74 fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
75 let name = pascal!(self.name => token_stream);
76 let prop_names: Vec<&syn::Ident> = self.props.iter().map(|(n, _)| n).collect();
77 let prop_values: Vec<&TokenStream> = self.props.iter().map(|(_, v)| v).collect();
78
79 if let Some(children) = &self.children {
80 let inner = idents.next().unwrap();
81 let children_expanded = children.expand(&inner, idents);
82
83 quote! {
84 {
85 let mut #inner = ::zyn::proc_macro2::TokenStream::new();
86 #children_expanded
87 let __zyn_rendered = ::zyn::Render::render(&#name {
88 #(#prop_names: #prop_values,)*
89 children: #inner,
90 }, &::zyn::Input::from(input.clone()));
91 let (__zyn_tokens, __zyn_diag) = __zyn_rendered.into_parts();
92 #output.extend(__zyn_tokens);
93 __zyn_diagnostic = __zyn_diagnostic.add(__zyn_diag);
94 }
95 }
96 } else {
97 quote! {
98 {
99 let __zyn_rendered = ::zyn::Render::render(&#name {
100 #(#prop_names: #prop_values,)*
101 }, &::zyn::Input::from(input.clone()));
102 let (__zyn_tokens, __zyn_diag) = __zyn_rendered.into_parts();
103 #output.extend(__zyn_tokens);
104 __zyn_diagnostic = __zyn_diagnostic.add(__zyn_diag);
105 }
106 }
107 }
108 }
109}
110
111impl Parse for ElementNode {
112 fn parse(input: ParseStream) -> syn::Result<Self> {
113 let first_ident: syn::Ident = input.parse()?;
114 let span = first_ident.span();
115
116 let mut name = TokenStream::new();
117 first_ident.to_tokens(&mut name);
118
119 while input.peek(Token![::]) {
120 let colons: Token![::] = input.parse()?;
121 colons.to_tokens(&mut name);
122 let segment: syn::Ident = input.parse()?;
123 segment.to_tokens(&mut name);
124 }
125
126 parse_props_and_children(input, span, name)
127 }
128}
129
130fn parse_props_and_children(
131 input: ParseStream,
132 span: Span,
133 name: TokenStream,
134) -> syn::Result<ElementNode> {
135 let mut props = Vec::new();
136
137 if input.peek(syn::token::Paren) {
138 let props_content;
139 syn::parenthesized!(props_content in input);
140
141 while !props_content.is_empty() {
142 let prop_name: syn::Ident = props_content.parse()?;
143 props_content.parse::<Token![=]>()?;
144
145 let mut value = TokenStream::new();
146
147 while !props_content.is_empty() && !props_content.peek(Token![,]) {
148 let tt: TokenTree = props_content.parse()?;
149 tt.to_tokens(&mut value);
150 }
151
152 props.push((prop_name, value));
153
154 if props_content.peek(Token![,]) {
155 props_content.parse::<Token![,]>()?;
156 }
157 }
158 }
159
160 let children = if input.peek(syn::token::Brace) {
161 let children_content;
162 syn::braced!(children_content in input);
163 Some(Box::new(children_content.parse::<Template>()?))
164 } else {
165 None
166 };
167
168 Ok(ElementNode {
169 span,
170 name,
171 props,
172 children,
173 })
174}