workflow_html_macros/
lib.rs1use proc_macro::TokenStream;
7use quote::quote;
8use syn::{
9 DeriveInput, Meta, Token,
10 ext::IdentExt,
11 parse::{Parse, ParseStream},
12 parse_macro_input,
13 punctuated::Punctuated,
14};
15mod element;
16mod attributes;
18use element::Nodes;
19use attributes::{AttributeName, AttributeNameString};
21use proc_macro_error3::proc_macro_error;
22
23#[proc_macro]
27#[proc_macro_error]
28pub fn tree(input: TokenStream) -> TokenStream {
29 let nodes = parse_macro_input!(input as Nodes);
30 let ts = quote! {#nodes};
31 ts.into()
33}
34
35#[proc_macro]
39#[proc_macro_error]
40pub fn html(input: TokenStream) -> TokenStream {
41 let nodes = parse_macro_input!(input as Nodes);
42 let ts = quote! {#nodes};
43 quote!({
45 let elements = #ts;
46
47 elements.render_tree()
48 })
49 .into()
50}
51
52#[proc_macro]
55#[proc_macro_error]
56pub fn html_str(input: TokenStream) -> TokenStream {
57 let nodes = parse_macro_input!(input as Nodes);
58 let ts = quote! {#nodes};
59 quote!({
61 let elements = #ts;
62
63 elements.html()
64 })
65 .into()
66}
67
68struct RenderableAttributes {
69 pub tag_name: String,
70}
71
72impl Parse for RenderableAttributes {
73 fn parse(input: ParseStream) -> syn::Result<Self> {
74 let tag_name = AttributeName::parse_separated_nonempty_with(input, syn::Ident::parse_any)?;
75 Ok(RenderableAttributes {
76 tag_name: tag_name.to_string(),
77 })
78 }
79}
80
81#[proc_macro_attribute]
91#[proc_macro_error]
93pub fn renderable(attr: TokenStream, item: TokenStream) -> TokenStream {
94 let renderable_attr = parse_macro_input!(attr as RenderableAttributes);
95 let tag_name = renderable_attr.tag_name;
96 let format_str = format!("<{tag_name} {{}}>{{}}</{tag_name}>");
97 let ast = parse_macro_input!(item as DeriveInput);
100 let struct_name = &ast.ident;
101 let struct_params = &ast.generics;
102 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
103 let mut field_visibility_vec = vec![];
113 let mut field_ident_vec = vec![];
114 let mut field_type_vec = vec![];
115 let mut attrs_ts_vec = vec![];
116 let mut field_names: Vec<String> = vec![];
117
118 if let syn::Data::Struct(syn::DataStruct {
120 fields: syn::Fields::Named(ref fields),
121 ..
122 }) = ast.data
123 {
124 for field in fields.named.iter() {
126 let field_name: syn::Ident = field.ident.as_ref().unwrap().clone();
127 field_ident_vec.push(&field.ident);
128 field_visibility_vec.push(&field.vis);
129 field_type_vec.push(&field.ty);
130 let mut attr_name = field_name.to_string();
131 if attr_name.eq("children") {
132 continue;
134 }
135 field_names.push(attr_name.clone());
136 let mut attrs: Vec<_> = field.attrs.iter().collect();
140
141 if !attrs.is_empty() {
142 let attr = attrs.remove(0);
143 if let Meta::List(list) = &attr.meta {
144 let nested = list
145 .parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
146 .unwrap();
147 for item in nested.iter() {
148 if let Meta::NameValue(name_value) = item {
149 let key = name_value.path.get_ident().unwrap().to_string();
150 let value: String = match &name_value.value {
151 syn::Expr::Lit(syn::ExprLit { lit, .. }) => match lit {
152 syn::Lit::Int(v) => v.to_string(),
153 syn::Lit::Str(v) => v.value(),
154 syn::Lit::Bool(v) => v.value().to_string(),
155 _ => "".to_string(),
156 },
157 _ => "".to_string(),
158 };
159 if key.eq("name") {
161 attr_name = value;
162 }
163 }
164 }
165 }
166 }
167
168 let field_type = match &field.ty {
169 syn::Type::Path(a) => match a.path.get_ident() {
170 Some(a) => a.to_string(),
171 None => {
172 if !a.path.segments.is_empty() {
173 a.path.segments[0].ident.to_string()
174 } else {
175 "".to_string()
176 }
177 }
178 },
179 syn::Type::Reference(a) => match a.elem.as_ref() {
180 syn::Type::Path(a) => match a.path.get_ident() {
181 Some(a) => a.to_string(),
182 None => {
183 if !a.path.segments.is_empty() {
184 a.path.segments[0].ident.to_string()
185 } else {
186 "".to_string()
187 }
188 }
189 },
190 _ => "".to_string(),
191 },
192 _ => "".to_string(),
193 };
194
195 if field_type.eq("bool") {
196 let fmt_str = attr_name.to_string();
197 attrs_ts_vec.push(quote!(
198 if self.#field_name{
199 attrs.push(#fmt_str.to_string());
200 }
201 ));
202 } else {
203 let fmt_str = format!("{attr_name}=\"{{}}\"");
204 let mut borrow = quote!();
205 if field_type.eq("String") {
206 borrow = quote!(&);
207 }
208 if field_type.eq("Option") {
209 attrs_ts_vec.push(quote!(
210 match &self.#field_name{
211 Some(value)=>{
212 attrs.push(format!(#fmt_str, workflow_html::escape_attr(value)));
213 }
214 None=>{
215
216 }
217 }
218 ));
219 } else {
220 attrs_ts_vec.push(quote!(
221 attrs.push(format!(#fmt_str, workflow_html::escape_attr(#borrow self.#field_name)));
222 ));
223 }
224 }
225 }
226 }
232
233 let ts = quote!(
235 #[derive(Clone, Default)]
236 pub struct #struct_name #struct_params #where_clause {
237 #( #field_visibility_vec #field_ident_vec : #field_type_vec ),*,
238 }
240
241 impl #impl_generics workflow_html::Render for #struct_name #type_generics #where_clause {
242 fn render(&self, w:&mut Vec<String>)->workflow_html::ElementResult<()>{
243 let attr = self.get_attributes();
244 let children = self.get_children();
245 w.push(format!(#format_str, attr, children));
246 Ok(())
247 }
248 }
249 impl #impl_generics workflow_html::ElementDefaults for #struct_name #type_generics #where_clause {
250 fn _get_attributes(&self)->String{
251 let mut attrs:Vec<String> = vec![];
252 #(#attrs_ts_vec)*
253 attrs.join(" ")
254 }
255 fn _get_children(&self)->String{
256 match &self.children{
257 Some(children)=>{
258 children.html()
259 }
260 None=>{
261 "".to_string()
262 }
263 }
264 }
265 }
266 );
267 ts.into()
269}