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// }