workflow_html_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    ext::IdentExt,
5    parse::{Parse, ParseStream},
6    parse_macro_input, DeriveInput, Meta, NestedMeta,
7};
8mod element;
9//mod state;
10mod attributes;
11use element::Nodes;
12//use state::set_attributes;
13use attributes::{AttributeName, AttributeNameString};
14use proc_macro_error::proc_macro_error;
15
16#[proc_macro]
17#[proc_macro_error]
18pub fn tree(input: TokenStream) -> TokenStream {
19    let nodes = parse_macro_input!(input as Nodes);
20    let ts = quote! {#nodes};
21    //println!("\n===========> Nodes Object tree <===========\n{}\n", ts.to_string());
22    ts.into()
23}
24
25#[proc_macro]
26#[proc_macro_error]
27pub fn html(input: TokenStream) -> TokenStream {
28    let nodes = parse_macro_input!(input as Nodes);
29    let ts = quote! {#nodes};
30    //println!("\n===========> Nodes Object tree <===========\n{}\n", ts.to_string());
31    quote!({
32        let elements = #ts;
33
34        elements.render_tree()
35    })
36    .into()
37}
38
39#[proc_macro]
40#[proc_macro_error]
41pub fn html_str(input: TokenStream) -> TokenStream {
42    let nodes = parse_macro_input!(input as Nodes);
43    let ts = quote! {#nodes};
44    //println!("\n===========> Nodes Object tree <===========\n{}\n", ts.to_string());
45    quote!({
46        let elements = #ts;
47
48        elements.html()
49    })
50    .into()
51}
52
53struct RenderableAttributes {
54    pub tag_name: String,
55}
56
57impl Parse for RenderableAttributes {
58    fn parse(input: ParseStream) -> syn::Result<Self> {
59        let tag_name = AttributeName::parse_separated_nonempty_with(input, syn::Ident::parse_any)?;
60        Ok(RenderableAttributes {
61            tag_name: tag_name.to_string(),
62        })
63    }
64}
65
66#[proc_macro_attribute]
67//#[proc_macro_derive(Renderable)]
68#[proc_macro_error]
69pub fn renderable(attr: TokenStream, item: TokenStream) -> TokenStream {
70    let renderable_attr = parse_macro_input!(attr as RenderableAttributes);
71    let tag_name = renderable_attr.tag_name;
72    let format_str = format!("<{tag_name} {{}}>{{}}</{tag_name}>");
73    //println!("renderable_attr: {:?}", tag_name);
74    //let def:proc_macro2::TokenStream = item.clone().into();
75    let ast = parse_macro_input!(item as DeriveInput);
76    let struct_name = &ast.ident;
77    let struct_params = &ast.generics;
78    let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
79    /*let generics_only = ast.generics.clone();
80    let where_clause = match generics_only.where_clause.clone() {
81        Some(where_clause) => quote!{ #where_clause },
82        None => quote!{}
83    };
84    */
85
86    //println!("struct_params: {:#?}", struct_params);
87
88    let mut field_visibility_vec = vec![];
89    let mut field_ident_vec = vec![];
90    let mut field_type_vec = vec![];
91    let mut attrs_ts_vec = vec![];
92    let mut field_names: Vec<String> = vec![];
93
94    //let mut children_field_ts = quote!();
95    if let syn::Data::Struct(syn::DataStruct {
96        fields: syn::Fields::Named(ref fields),
97        ..
98    }) = ast.data
99    {
100        //let mut has_children_field = false;
101        for field in fields.named.iter() {
102            let field_name: syn::Ident = field.ident.as_ref().unwrap().clone();
103            field_ident_vec.push(&field.ident);
104            field_visibility_vec.push(&field.vis);
105            field_type_vec.push(&field.ty);
106            let mut attr_name = field_name.to_string();
107            if attr_name.eq("children") {
108                //has_children_field = true;
109                continue;
110            }
111            field_names.push(attr_name.clone());
112            //let name: String = field_name.to_string();
113            //println!("\n\n----->name: {}, \ntype: {:?}, \nattrs: {:?}", field_name, field.ty, field.attrs);
114            //println!("\n\n----->name: {}, \ntype: {:?}", field_name, field.ty);
115            let mut attrs: Vec<_> = field.attrs.iter().collect();
116
117            if !attrs.is_empty() {
118                let attr = attrs.remove(0);
119                let meta = attr.parse_meta().unwrap();
120                if let Meta::List(list) = meta {
121                    //println!("meta-list: {:#?}", list);
122                    //println!("meta-list.path: {:#?}", list.path.get_ident().unwrap().to_string());
123                    //println!("nested: {:?}", list.nested);
124                    for item in list.nested.iter() {
125                        if let NestedMeta::Meta(Meta::NameValue(name_value)) = item {
126                            let key = name_value.path.get_ident().unwrap().to_string();
127                            let value: String = match &name_value.lit {
128                                syn::Lit::Int(v) => v.to_string(),
129                                syn::Lit::Str(v) => v.value(),
130                                syn::Lit::Bool(v) => v.value().to_string(),
131                                _ => "".to_string(),
132                            };
133                            //println!("key: {}, value: {}", key, value);
134                            if key.eq("name") {
135                                attr_name = value;
136                            }
137                        }
138                    }
139                }
140            }
141
142            let field_type = match &field.ty {
143                syn::Type::Path(a) => match a.path.get_ident() {
144                    Some(a) => a.to_string(),
145                    None => {
146                        if !a.path.segments.is_empty() {
147                            a.path.segments[0].ident.to_string()
148                        } else {
149                            "".to_string()
150                        }
151                    }
152                },
153                syn::Type::Reference(a) => match a.elem.as_ref() {
154                    syn::Type::Path(a) => match a.path.get_ident() {
155                        Some(a) => a.to_string(),
156                        None => {
157                            if !a.path.segments.is_empty() {
158                                a.path.segments[0].ident.to_string()
159                            } else {
160                                "".to_string()
161                            }
162                        }
163                    },
164                    _ => "".to_string(),
165                },
166                _ => "".to_string(),
167            };
168
169            if field_type.eq("bool") {
170                let fmt_str = attr_name.to_string();
171                attrs_ts_vec.push(quote!(
172                    if self.#field_name{
173                        attrs.push(#fmt_str.to_string());
174                    }
175                ));
176            } else {
177                let fmt_str = format!("{attr_name}=\"{{}}\"");
178                let mut borrow = quote!();
179                if field_type.eq("String") {
180                    borrow = quote!(&);
181                }
182                if field_type.eq("Option") {
183                    attrs_ts_vec.push(quote!(
184                        match &self.#field_name{
185                            Some(value)=>{
186                                attrs.push(format!(#fmt_str, workflow_html::escape_attr(value)));
187                            }
188                            None=>{
189
190                            }
191                        }
192                    ));
193                } else {
194                    attrs_ts_vec.push(quote!(
195                        attrs.push(format!(#fmt_str, workflow_html::escape_attr(#borrow self.#field_name)));
196                    ));
197                }
198            }
199        }
200        //if !has_children_field{
201        //    children_field_ts = quote!(
202        //children:Option<R>
203        //    );
204        //}
205    }
206
207    //set_attributes(struct_name.to_string(), field_names);
208    let ts = quote!(
209        #[derive(Clone, Default)]
210        pub struct #struct_name #struct_params #where_clause {
211            #( #field_visibility_vec #field_ident_vec : #field_type_vec ),*,
212            //#children_field_ts
213        }
214
215        impl #impl_generics workflow_html::Render for #struct_name #type_generics #where_clause {
216            fn render(&self, w:&mut Vec<String>)->workflow_html::ElementResult<()>{
217                let attr = self.get_attributes();
218                let children = self.get_children();
219                w.push(format!(#format_str, attr, children));
220                Ok(())
221            }
222        }
223        impl #impl_generics workflow_html::ElementDefaults for #struct_name #type_generics #where_clause {
224            fn _get_attributes(&self)->String{
225                let mut attrs:Vec<String> = vec![];
226                #(#attrs_ts_vec)*
227                attrs.join(" ")
228            }
229            fn _get_children(&self)->String{
230                match &self.children{
231                    Some(children)=>{
232                        children.html()
233                    }
234                    None=>{
235                        "".to_string()
236                    }
237                }
238            }
239        }
240    );
241    //println!("\n===========> element({}) ts: <===========\n{}", struct_name, ts);
242    ts.into()
243}