use proc_macro::TokenStream;
use quote::quote;
use syn::{
DeriveInput, Meta, Token,
ext::IdentExt,
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
};
mod element;
mod attributes;
use element::Nodes;
use attributes::{AttributeName, AttributeNameString};
use proc_macro_error3::proc_macro_error;
#[proc_macro]
#[proc_macro_error]
pub fn tree(input: TokenStream) -> TokenStream {
let nodes = parse_macro_input!(input as Nodes);
let ts = quote! {#nodes};
ts.into()
}
#[proc_macro]
#[proc_macro_error]
pub fn html(input: TokenStream) -> TokenStream {
let nodes = parse_macro_input!(input as Nodes);
let ts = quote! {#nodes};
quote!({
let elements = #ts;
elements.render_tree()
})
.into()
}
#[proc_macro]
#[proc_macro_error]
pub fn html_str(input: TokenStream) -> TokenStream {
let nodes = parse_macro_input!(input as Nodes);
let ts = quote! {#nodes};
quote!({
let elements = #ts;
elements.html()
})
.into()
}
struct RenderableAttributes {
pub tag_name: String,
}
impl Parse for RenderableAttributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
let tag_name = AttributeName::parse_separated_nonempty_with(input, syn::Ident::parse_any)?;
Ok(RenderableAttributes {
tag_name: tag_name.to_string(),
})
}
}
#[proc_macro_attribute]
#[proc_macro_error]
pub fn renderable(attr: TokenStream, item: TokenStream) -> TokenStream {
let renderable_attr = parse_macro_input!(attr as RenderableAttributes);
let tag_name = renderable_attr.tag_name;
let format_str = format!("<{tag_name} {{}}>{{}}</{tag_name}>");
let ast = parse_macro_input!(item as DeriveInput);
let struct_name = &ast.ident;
let struct_params = &ast.generics;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
let mut field_visibility_vec = vec![];
let mut field_ident_vec = vec![];
let mut field_type_vec = vec![];
let mut attrs_ts_vec = vec![];
let mut field_names: Vec<String> = vec![];
if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
..
}) = ast.data
{
for field in fields.named.iter() {
let field_name: syn::Ident = field.ident.as_ref().unwrap().clone();
field_ident_vec.push(&field.ident);
field_visibility_vec.push(&field.vis);
field_type_vec.push(&field.ty);
let mut attr_name = field_name.to_string();
if attr_name.eq("children") {
continue;
}
field_names.push(attr_name.clone());
let mut attrs: Vec<_> = field.attrs.iter().collect();
if !attrs.is_empty() {
let attr = attrs.remove(0);
if let Meta::List(list) = &attr.meta {
let nested = list
.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
.unwrap();
for item in nested.iter() {
if let Meta::NameValue(name_value) = item {
let key = name_value.path.get_ident().unwrap().to_string();
let value: String = match &name_value.value {
syn::Expr::Lit(syn::ExprLit { lit, .. }) => match lit {
syn::Lit::Int(v) => v.to_string(),
syn::Lit::Str(v) => v.value(),
syn::Lit::Bool(v) => v.value().to_string(),
_ => "".to_string(),
},
_ => "".to_string(),
};
if key.eq("name") {
attr_name = value;
}
}
}
}
}
let field_type = match &field.ty {
syn::Type::Path(a) => match a.path.get_ident() {
Some(a) => a.to_string(),
None => {
if !a.path.segments.is_empty() {
a.path.segments[0].ident.to_string()
} else {
"".to_string()
}
}
},
syn::Type::Reference(a) => match a.elem.as_ref() {
syn::Type::Path(a) => match a.path.get_ident() {
Some(a) => a.to_string(),
None => {
if !a.path.segments.is_empty() {
a.path.segments[0].ident.to_string()
} else {
"".to_string()
}
}
},
_ => "".to_string(),
},
_ => "".to_string(),
};
if field_type.eq("bool") {
let fmt_str = attr_name.to_string();
attrs_ts_vec.push(quote!(
if self.#field_name{
attrs.push(#fmt_str.to_string());
}
));
} else {
let fmt_str = format!("{attr_name}=\"{{}}\"");
let mut borrow = quote!();
if field_type.eq("String") {
borrow = quote!(&);
}
if field_type.eq("Option") {
attrs_ts_vec.push(quote!(
match &self.#field_name{
Some(value)=>{
attrs.push(format!(#fmt_str, workflow_html::escape_attr(value)));
}
None=>{
}
}
));
} else {
attrs_ts_vec.push(quote!(
attrs.push(format!(#fmt_str, workflow_html::escape_attr(#borrow self.#field_name)));
));
}
}
}
}
let ts = quote!(
#[derive(Clone, Default)]
pub struct #struct_name #struct_params #where_clause {
#( #field_visibility_vec #field_ident_vec : #field_type_vec ),*,
}
impl #impl_generics workflow_html::Render for #struct_name #type_generics #where_clause {
fn render(&self, w:&mut Vec<String>)->workflow_html::ElementResult<()>{
let attr = self.get_attributes();
let children = self.get_children();
w.push(format!(#format_str, attr, children));
Ok(())
}
}
impl #impl_generics workflow_html::ElementDefaults for #struct_name #type_generics #where_clause {
fn _get_attributes(&self)->String{
let mut attrs:Vec<String> = vec![];
#(#attrs_ts_vec)*
attrs.join(" ")
}
fn _get_children(&self)->String{
match &self.children{
Some(children)=>{
children.html()
}
None=>{
"".to_string()
}
}
}
}
);
ts.into()
}