workflow_html_macros/
lib.rs1use 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;
9mod attributes;
11use element::Nodes;
12use 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 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 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 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_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 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 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 if let syn::Data::Struct(syn::DataStruct {
96 fields: syn::Fields::Named(ref fields),
97 ..
98 }) = ast.data
99 {
100 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 continue;
110 }
111 field_names.push(attr_name.clone());
112 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 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 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 }
206
207 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 }
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 ts.into()
243}