Skip to main content

cheers_ast/
component.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote, quote_spanned};
3use syn::{
4    Ident, LitBool, LitChar, LitFloat, LitInt, LitStr, Token,
5    ext::IdentExt,
6    parse::{Parse, ParseStream},
7    spanned::Spanned,
8    token::{Brace, Bracket, Paren},
9};
10
11use super::{ElementBody, Generate, Generator, Literal, ParenExpr, ParenExprMode};
12use crate::{AttributeValueNode, Context, SyntaxStatic};
13
14pub struct Component {
15    pub name: Ident,
16    pub attrs: Vec<ComponentAttribute>,
17    pub default_attrs: Option<ComponentDefaultAttributes>,
18    pub dotdot: Option<Token![..]>,
19    pub body: ElementBody,
20}
21
22impl SyntaxStatic for Component {
23    fn is_static(&self) -> bool {
24        false
25    }
26}
27
28impl Component {
29    fn children_lazy(&mut self, g: &mut Generator<'_>) -> Option<TokenStream> {
30        match &mut self.body {
31            ElementBody::Normal { children, .. } => {
32                let buffer_ident = Generator::buffer_ident();
33
34                let block = g.block_with(
35                    Brace::default(),
36                    |g| {
37                        g.push(children);
38                    },
39                    true,
40                );
41
42                Some(quote! {
43                    ::cheers::prelude::Lazy::dangerously_create(
44                        |#buffer_ident: &mut ::cheers::prelude::Buffer|
45                            #block
46                    )
47                })
48            }
49            ElementBody::Void { .. } => None,
50        }
51    }
52
53    fn default_setters(&self, g: &mut Generator<'_>) -> Vec<TokenStream> {
54        let mut setters = Vec::new();
55
56        if let Some(default_attrs) = &self.default_attrs {
57            for attr in &default_attrs.attrs {
58                let name = &attr.name;
59                let value = attr.value_expr(g);
60
61                setters.push(quote!(.#name(#value)));
62            }
63        }
64
65        setters
66    }
67
68    fn required_attrs_in_signature_order(&self) -> Vec<&ComponentAttribute> {
69        let mut attrs = self.attrs.iter().collect::<Vec<_>>();
70        attrs.sort_by_key(|attr| attr.name.unraw().to_string());
71        attrs
72    }
73
74    fn build_suffix(children_lazy: Option<TokenStream>) -> TokenStream {
75        match children_lazy {
76            Some(children_lazy) => quote!(.__cheers_build_with_children(#children_lazy)),
77            None => quote!(.__cheers_build()),
78        }
79    }
80
81    fn generate_dotdot_tokens(&mut self, g: &mut Generator<'_>) -> TokenStream {
82        let fields = self
83            .attrs
84            .iter()
85            .map(|attr| {
86                let name = &attr.name;
87                let value = attr.value_expr(g);
88
89                quote!(#name: #value,)
90            })
91            .collect::<Vec<_>>();
92
93        let children = self.children_lazy(g).map(|children| {
94            let children_ident = Ident::new("children", self.name.span());
95            quote!(#children_ident: #children,)
96        });
97
98        let name = &self.name;
99        let default = self
100            .dotdot
101            .as_ref()
102            .map(|dotdot| quote_spanned!(dotdot.span()=> ..::core::default::Default::default()))
103            .unwrap_or_default();
104
105        quote! {
106            #name {
107                #(#fields)*
108                #children
109                #default
110            }
111        }
112    }
113
114    fn generate_prop_builder_tokens(&mut self, g: &mut Generator<'_>) -> TokenStream {
115        let required_attrs = self
116            .required_attrs_in_signature_order()
117            .into_iter()
118            .map(|attr| {
119                let field = attr.name.clone();
120                let method = Ident::new(
121                    &format!("__cheers_prop_{}", attr.name.unraw()),
122                    attr.name.span(),
123                );
124                let value = attr.value_expr(g);
125
126                (field, method, value)
127            });
128
129        let required_attrs = required_attrs.collect::<Vec<_>>();
130        let default_setters = self.default_setters(g);
131        let build_suffix = Self::build_suffix(self.children_lazy(g));
132
133        let name = &self.name;
134        let runtime_required_constructors = required_attrs
135            .iter()
136            .map(|(_, method, value)| quote!(#name::#method(#value)));
137
138        let required_assignments = required_attrs.iter().map(|(field, _, value)| {
139            quote! {
140                __cheers_required.#field = #value;
141            }
142        });
143
144        let runtime_constructor = quote! {
145            #name::__cheers_props(#(#runtime_required_constructors),*)
146            #(#default_setters)*
147        };
148
149        let ra_constructor = quote! {
150            {
151                let mut __cheers_required = #name::__cheers_required();
152                #(#required_assignments)*
153                #name::__cheers_props_from_required(__cheers_required)
154                #(#default_setters)*
155            }
156        };
157
158        quote! {
159            {
160                #[allow(unexpected_cfgs, unused_parens)]
161                let __cheers_component = {
162                    #[cfg(rust_analyzer)]
163                    {
164                        #ra_constructor
165                        #build_suffix
166                    }
167
168                    #[cfg(not(rust_analyzer))]
169                    {
170                        #runtime_constructor
171                        #build_suffix
172                    }
173                };
174
175                __cheers_component
176            }
177        }
178    }
179}
180
181impl Generate for Component {
182    const CONTEXT: Context = Context::Element;
183
184    fn generate(&mut self, g: &mut Generator<'_>) {
185        let tokens = if self.default_attrs.is_some() && self.dotdot.is_none() {
186            self.generate_prop_builder_tokens(g)
187        } else {
188            self.generate_dotdot_tokens(g)
189        };
190        g.push_expr(Paren::default(), Self::CONTEXT, tokens);
191    }
192}
193
194pub struct ComponentDefaultAttributes {
195    pub bracket_token: Bracket,
196    pub attrs: Vec<ComponentAttribute>,
197}
198
199impl Parse for ComponentDefaultAttributes {
200    fn parse(input: ParseStream) -> syn::Result<Self> {
201        let content;
202
203        Ok(Self {
204            bracket_token: syn::bracketed!(content in input),
205            attrs: {
206                let mut attrs = Vec::new();
207
208                while !content.is_empty() {
209                    attrs.push(content.parse()?);
210                }
211
212                attrs
213            },
214        })
215    }
216}
217
218pub struct ComponentAttribute {
219    pub name: Ident,
220    pub value: Option<ComponentAttributeValue>,
221}
222
223impl ComponentAttribute {
224    pub(crate) fn value_expr(&self, g: &mut Generator<'_>) -> TokenStream {
225        match &self.value {
226            Some(ComponentAttributeValue::Literal(lit)) => lit.to_token_stream(),
227            Some(ComponentAttributeValue::Expr(expr)) => match expr.mode {
228                ParenExprMode::Normal => {
229                    let mut tokens = TokenStream::new();
230
231                    expr.paren_token.surround(&mut tokens, |tokens| {
232                        expr.body.to_tokens(tokens);
233                    });
234
235                    tokens
236                }
237                ParenExprMode::Ref => g
238                    .hoist_ref_expr(expr.paren_token, &expr.body)
239                    .to_token_stream(),
240            },
241            Some(ComponentAttributeValue::Ident(ident)) => ident.to_token_stream(),
242            None => self.name.to_token_stream(),
243        }
244    }
245}
246
247impl Parse for ComponentAttribute {
248    fn parse(input: ParseStream) -> syn::Result<Self> {
249        let name = input.parse()?;
250        Ok(Self {
251            name,
252            value: {
253                if input.peek(Token![=]) {
254                    input.parse::<Token![=]>()?;
255                    Some(input.parse()?)
256                } else {
257                    None
258                }
259            },
260        })
261    }
262}
263
264#[allow(clippy::large_enum_variant)]
265pub enum ComponentAttributeValue {
266    Literal(Literal),
267    Ident(Ident),
268    Expr(ParenExpr<AttributeValueNode>),
269}
270
271impl Parse for ComponentAttributeValue {
272    fn parse(input: ParseStream) -> syn::Result<Self> {
273        let lookahead = input.lookahead1();
274
275        if lookahead.peek(LitStr)
276            || lookahead.peek(LitInt)
277            || lookahead.peek(LitBool)
278            || lookahead.peek(LitFloat)
279            || lookahead.peek(LitChar)
280        {
281            input.call(Literal::parse_any).map(Self::Literal)
282        } else if lookahead.peek(Ident) {
283            input.parse().map(Self::Ident)
284        } else if lookahead.peek(Paren) {
285            input.parse().map(Self::Expr)
286        } else {
287            Err(lookahead.error())
288        }
289    }
290}