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}