dioxus_tw_components_macro/
lib.rs

1mod deriveuicomp;
2
3use deriveuicomp::impl_my_derive;
4use proc_macro::TokenStream;
5use syn::parse_macro_input;
6
7/// This macro is used to derive the UiComp trait on a Props, which will also implement
8/// std::fmt::Display by getting only what is before the "Props" in the struct,
9/// HasChildren by parsing the struct and checking if a children field is present
10/// BuildClass by also parsing the struct and checking if a attributes field is present (which is a Vec<Attribute> which can contain the "class" Attribute)
11/// BuildClass also force to implement Class on your Props, which is used for styling
12#[proc_macro_derive(UiComp)]
13pub fn derive_ui_comp(input: TokenStream) -> TokenStream {
14    let ast = parse_macro_input!(input as syn::DeriveInput);
15
16    impl_my_derive(&ast)
17}
18
19// Legacy code, old macro used to generate Props struct, not in use anymore
20
21// /// This procedural macro generates a new struct and function based on the input function.
22// /// The generated struct will have the same name as the input function with "Props" appended to it.
23// /// The struct will contain fields for each input parameter of the function and additional fields
24// /// specified in the macro arguments. The generated function will take an instance of the generated
25// /// struct as its only parameter and execute the same code as the input function. The goal was to make easier
26// /// the generation of component for the Dioxus web framework and to have some general attributes to be added
27// /// almost automatically and in the same way everywhere. For now we handle 3 attributes of this kind, these being
28// /// 1. id: *String* => used to give an id to the component
29// /// 2. class: *String* => used add custom style to the component, will add class and class_override to override the whole
30// /// class of the component
31// /// 3. children: *Element* => used to pass something to render as a child to the component \
32// /// **Example**
33// /// ```ignore
34// /// /// This is a cool and very useful component
35// /// #[props_component(id, class, children)]
36// /// pub fn NewElement(
37// /// /// This is to color the component
38// /// #[props(default = Color::Destructive)] color: Color
39// /// ) -> Element
40// ///
41// /// // Will expand to this struct
42// ///
43// /// #[derive(Clone, Props, PartialEq)]
44// /// pub struct NewElementProps {
45// ///     /// This is to color this cool component
46// ///     #[props(default = Color::Destructive)]
47// ///     pub color: Color,
48// ///     #[props(into)]
49// ///     #[props(optional)]
50// ///     pub id: String,
51// ///     #[props(into)]
52// ///     #[props(default)]
53// ///     pub class: String,
54// ///     #[props(into, optional)]
55// ///     pub class_override: String,
56// ///     pub children: Element
57// /// }
58// ///
59// /// // and the function signature will be changed to this
60// ///
61// /// /// This is a cool and very useful component
62// /// pub fn NewElement(props: NewElementProps) -> Element
63// /// ```
64// ///
65// #[proc_macro_attribute]
66// pub fn props_component(args: TokenStream, input: TokenStream) -> TokenStream {
67//     let input = parse_macro_input!(input as ItemFn);
68
69//     let mut vec_attr = Vec::new();
70//     let mut vec_attr_props = Vec::new();
71
72//     for attr in &input.attrs {
73//         // Check if the attribute of the input(the function) is a doc comment
74//         if attr.path().is_ident("doc") {
75//             vec_attr.push(attr.clone());
76//         } else if attr.path().is_ident("derive") {
77//             vec_attr_props.push(attr.clone());
78//         }
79//     }
80
81//     let name = &input.sig.ident;
82//     let name_struct = syn::Ident::new(&format!("{}Props", name), proc_macro2::Span::call_site());
83
84//     // Let statement are not used right now since they made the code far more complex to resolve problems about borrowing and such
85//     // any_mut is used to check if any of the parameters of the function is mutable, if so we will make the props mutable
86//     let (mut fields, any_mut) = make_struct_fields(&input.sig.inputs);
87
88//     let args = parse_macro_input!(args as ParsedArg);
89//     let accepted_attributes = AttributeConfig::default();
90
91//     let mut has_class_attr = false;
92//     let mut has_children_attr = false;
93
94//     for arg in args.args {
95//         match accepted_attributes.accepted(arg.to_string()) {
96//             Ok(index) => {
97//                 fields.push(accepted_attributes.add_attributes[index].field.clone());
98//                 if arg.to_string() == "class" {
99//                     has_class_attr = true;
100//                 }
101//                 if arg.to_string() == "children" {
102//                     has_children_attr = true;
103//                 }
104//             }
105//             Err(e) => panic!("{e}"),
106//         }
107//     }
108
109//     let output = &input.sig.output;
110//     let block = &input.block;
111
112//     let props = match any_mut {
113//         true => quote! { mut props: #name_struct },
114//         false => quote! {
115//             mut props: #name_struct
116//         },
117//     };
118
119//     // If the class attribute is found we also derive BuildClass, a macro which automatically build the final class of the props
120//     let derive = if has_class_attr {
121//         quote! {#[derive(Clone, Props, PartialEq, BuildClass, Default)]}
122//     } else {
123//         quote! {#[derive(Clone, Props, PartialEq, Default)]}
124//     };
125
126//     let build_class = if has_class_attr {
127//         quote! {props.build_class();}
128//     } else {
129//         quote! {}
130//     };
131
132//     let mut named = String::new();
133//     for (i, c) in name.to_string().chars().enumerate() {
134//         if i > 0 && c.is_uppercase() {
135//             named.push(' ');
136//         }
137//         named.push(c);
138//     }
139
140//     let impl_named = quote! {
141//         impl Named for #name_struct {
142//             const NAME: &'static str = "#named";
143//         }
144//     };
145
146//     let has_children_impl = if has_children_attr {
147//         quote! {
148//             impl HasChildren for #name_struct {
149//                 fn has_children(&self) -> bool {
150//                     true
151//                 }
152
153//                 fn set_children(&mut self, children: Element) {
154//                     self.children = children;
155//                 }
156//             }
157//         }
158//     } else {
159//         quote! {
160//             impl HasChildren for #name_struct {}
161//         }
162//     };
163
164//     let expanded = quote! {
165
166//         #derive
167//         #(#vec_attr_props)*
168//         pub struct #name_struct {
169//             #(#fields),*
170//         }
171
172//         #impl_named
173
174//         #has_children_impl
175
176//         #(#vec_attr)*
177//         pub fn #name(#props) #output {
178//             #build_class
179//             let result = (|| #block)();
180//             result
181//         }
182//     };
183
184//     TokenStream::from(expanded)
185// }
186
187// /// This function takes a list of function parameters and generates a list of struct fields and
188// /// let statements for each parameter. The generated struct fields and let statements are used in
189// /// the props_component macro to generate the new struct and function.
190// fn make_struct_fields(
191//     inputs: &syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>,
192// ) -> (Vec<proc_macro2::TokenStream>, bool) {
193//     let mut fields = Vec::new();
194//     let mut any_mut = false;
195
196//     for input in inputs {
197//         if let syn::FnArg::Typed(pat_type) = input {
198//             let ident = match &*pat_type.pat {
199//                 syn::Pat::Ident(pat_ident) => {
200//                     if pat_ident.mutability.is_some() {
201//                         any_mut = true;
202//                     }
203//                     &pat_ident.ident
204//                 }
205//                 _ => panic!("Unsupported parameter pattern"),
206//             };
207
208//             let ty = &pat_type.ty;
209//             let attr = &pat_type.attrs;
210
211//             fields.push(quote! { #(#attr)*pub #ident: #ty });
212//         }
213//     }
214
215//     (fields, any_mut)
216// }
217
218// /// This struct represents a list of identifiers parsed from the arguments of the props_component
219// /// macro. The identifiers specify additional fields to be added to the generated struct.
220// #[derive(Debug)]
221// struct ParsedArg {
222//     args: Vec<syn::Ident>,
223// }
224
225// impl Parse for ParsedArg {
226//     fn parse(input: ParseStream) -> syn::Result<Self> {
227//         let args =
228//             syn::punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated(&input)?;
229
230//         Ok(ParsedArg {
231//             args: args.into_iter().collect(),
232//         })
233//     }
234// }
235
236// /// This struct represents a configuration of accepted attributes that can be added to the
237// /// generated struct in the props_component macro. Each accepted attribute has a name, a struct
238// /// field, and a let statement associated with it.
239// #[derive(Debug)]
240// struct AttributeConfig {
241//     add_attributes: Vec<AcceptedAttribute>,
242// }
243
244// impl AttributeConfig {
245//     fn default() -> Self {
246//         let mut add_attributes = Vec::new();
247
248//         add_attributes.push(AcceptedAttribute::id());
249//         add_attributes.push(AcceptedAttribute::class());
250//         add_attributes.push(AcceptedAttribute::children());
251
252//         AttributeConfig { add_attributes }
253//     }
254
255//     fn accepted(&self, attr_name: String) -> Result<usize, String> {
256//         for (index, attribute) in self.add_attributes.iter().enumerate() {
257//             if attribute.name == attr_name {
258//                 return Ok(index);
259//             }
260//         }
261
262//         Err(format!("Invalid attribute: {}", attr_name))
263//     }
264// }
265
266// /// This struct represents an accepted attribute that can be added to the generated struct in the
267// /// props_component macro. It contains the name of the attribute, the struct field representing the
268// /// attribute, and the let statement used to destructure the attribute from the props parameter in
269// /// the generated function.
270// #[derive(Debug, Clone)]
271// struct AcceptedAttribute {
272//     name: String,
273//     field: proc_macro2::TokenStream,
274// }
275
276// impl PartialEq for AcceptedAttribute {
277//     fn eq(&self, other: &Self) -> bool {
278//         self.name == other.name
279//     }
280// }
281
282// impl AcceptedAttribute {
283//     fn id() -> Self {
284//         AcceptedAttribute {
285//             name: "id".to_string(),
286//             field: quote! {
287//             /// Unique ID of the component
288//             #[props(into, optional)] pub id: String },
289//         }
290//     }
291
292//     fn class() -> Self {
293//         AcceptedAttribute {
294//             name: "class".to_string(),
295//             field: quote! {
296//             /// Custom added styling class for the component
297//             #[props(into, optional)] pub class: String,
298//             /// Override the whole class
299//             #[props(into, optional)] pub override_class: String },
300//         }
301//     }
302
303//     fn children() -> Self {
304//         AcceptedAttribute {
305//             name: "children".to_string(),
306//             field: quote! {
307//             /// Children of the component to render
308//             pub children: Element },
309//         }
310//     }
311// }
312
313// /// Derive macro to automaticaly build the final class of the component, doing so by parsing the props struct
314// #[proc_macro_derive(BuildClass)]
315// pub fn build_class_derive(input: TokenStream) -> TokenStream {
316//     // Parse the input tokens into a syntax tree
317//     let ast: syn::DeriveInput = syn::parse(input).unwrap();
318
319//     // Get the name of the struct
320//     let name = &ast.ident;
321
322//     // Get all the fields of the struct
323//     let struct_fields = if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
324//         fields
325//     } else {
326//         panic!("BuildClass can only be derived on structs");
327//     };
328
329//     // Iter on watched_fields and all the struct fields to only keep in watched one the one we want
330//     let mut watched_fields = WatchedFields::get_all_facultative()
331//         .into_iter()
332//         .filter(|watched_field| {
333//             struct_fields.iter().any(|struct_field| {
334//                 struct_field
335//                     .ident
336//                     .as_ref()
337//                     .map(|ident| ident.to_string() == watched_field.ident.to_string())
338//                     .unwrap_or(false)
339//             })
340//         })
341//         .collect::<Vec<WatchedFields>>();
342
343//     // Add self.base() and &self.class to tw_merge!()
344//     watched_fields.push(WatchedFields::get_base());
345//     watched_fields.push(WatchedFields::get_class());
346
347//     // Iter on left watched fields to construst the tw_merge!() method
348//     let str = watched_fields
349//         .iter()
350//         .map(|field| field.method.to_string())
351//         .collect::<Vec<String>>()
352//         .join(",")
353//         .parse::<proc_macro2::TokenStream>()
354//         .unwrap();
355
356//     let set_methods = watched_fields
357//         .iter()
358//         .map(|field| field.set_method.clone())
359//         .collect::<Vec<proc_macro2::TokenStream>>();
360
361//     let gen = quote! {
362//         impl BuildClass for #name {
363//             fn build_class(&mut self) {
364//                 if !self.override_class.is_empty() {
365//                     self.class = self.override_class.to_owned();
366//                     return;
367//                 }
368//                 self.class = tw_merge!(#str);
369//             }
370//             #(#set_methods)*
371//         }
372//     };
373
374//     gen.into()
375// }
376
377// #[derive(Debug)]
378// struct WatchedFields {
379//     ident: &'static str,
380//     method: &'static str,
381//     set_method: proc_macro2::TokenStream,
382// }
383
384// impl WatchedFields {
385//     fn get_all_facultative() -> Vec<Self> {
386//         vec![
387//             WatchedFields {
388//                 ident: "color",
389//                 method: "self.color().unwrap_or_default()",
390//                 set_method: quote! {
391//                     fn set_color(&mut self, color: Color) {
392//                         self.color = color;
393//                     }
394//                 },
395//             },
396//             WatchedFields {
397//                 ident: "size",
398//                 method: "self.size().unwrap_or_default()",
399//                 set_method: quote! {
400//                     fn set_size(&mut self, size: Size) {
401//                         self.size = size;
402//                     }
403//                 },
404//             },
405//             WatchedFields {
406//                 ident: "animation",
407//                 method: "self.animation().unwrap_or_default()",
408//                 set_method: quote! {
409//                     fn set_animation(&mut self, animation: Animation) {
410//                         self.animation = animation;
411//                     }
412//                 },
413//             },
414//             WatchedFields {
415//                 ident: "variant",
416//                 method: "self.variant().unwrap_or_default()",
417//                 set_method: quote! {},
418//             },
419//             WatchedFields {
420//                 ident: "orientation",
421//                 method: "self.orientation().unwrap_or_default()",
422//                 set_method: quote! {
423//                     fn set_orientation(&mut self, orientation: Orientation) {
424//                         self.orientation = orientation;
425//                     }
426//                 },
427//             },
428//         ]
429//     }
430
431//     fn get_base() -> Self {
432//         WatchedFields {
433//             ident: "base",
434//             method: "self.base()",
435//             set_method: quote! {},
436//         }
437//     }
438
439//     fn get_class() -> Self {
440//         WatchedFields {
441//             ident: "class",
442//             method: "&self.class",
443//             set_method: quote! {},
444//         }
445//     }
446// }