use convert_case::{Case, Casing};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_attribute]
pub fn binroots_enum(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let vis = input.vis;
let ident = input.ident;
let attrs = input.attrs;
let generics = input.generics;
let mut found = None;
let mut manual = false;
for a in attr {
manual = a.to_string() == "manual"
}
let variants = if let syn::Data::Enum(syn::DataEnum { variants, .. }) = input.data {
if manual {
variants.into_iter().map(|v| quote!(#v)).collect::<Vec<_>>()
} else {
variants
.into_iter()
.enumerate()
.map(|(i, v)| {
if (v.ident == "None"
|| v.ident == "Nothing"
|| v.ident == "Empty"
|| v.ident == "Default")
&& found.is_none()
{
found = Some(i)
}
if let Some(c) = found {
if c == i {
quote! {
#[default]
#v
}
} else {
quote!(#v)
}
} else {
quote!(#v)
}
})
.collect::<Vec<_>>()
}
} else {
panic!("#[binroots_enum] only supports enums.")
};
let output = quote! {
#[derive(Debug, Default, binroots::Serialize)]
#( #attrs )*
#vis enum #ident #generics {
#(
#variants
),*
}
};
output.into()
}
#[proc_macro_attribute]
pub fn binroots_struct(
_attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let struct_name = &input.ident;
let vis = &input.vis;
let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
..
}) = input.data
{
&fields.named
} else {
panic!("#[binroots_struct] only supports named struct fields")
};
let field_names = fields.iter().map(|field| {
let field_name = &field.ident.as_ref().unwrap();
let field_name_str = &field.ident.as_ref().unwrap().to_string();
let field_type = &field.ty;
quote! {
#field_name: binroots::field::BinrootsField<#field_name_str, #field_type>,
}
});
let field_initializers_new = fields.iter().map(|field| {
let field_name = &field.ident.as_ref().unwrap();
let field_name_str = &field.ident.as_ref().unwrap().to_string();
let field_type = &field.ty;
quote! {
#field_name: binroots::field::BinrootsField::<#field_name_str, #field_type>::new(#field_name),
}
});
let field_initializers_default = fields.iter().map(|field| {
let field_name = &field.ident.as_ref().unwrap();
let field_name_str = &field.ident.as_ref().unwrap().to_string();
let field_type = &field.ty;
quote! {
#field_name: binroots::field::BinrootsField::<#field_name_str, #field_type>::default(),
}
});
let struct_name_str = struct_name.to_string().to_case(Case::Kebab);
let output = quote! {
#[derive(Debug, binroots::Serialize)]
#vis struct #struct_name {
#( #field_names )*
}
impl #struct_name {
const ROOT_FOLDER: &'static str = #struct_name_str;
pub fn new(#fields) -> Self {
Self {
#( #field_initializers_new )*
}
}
pub fn save(&self) -> Result<(), binroots::save::SaveError> {
binroots::save::Save::save(self, Self::ROOT_FOLDER)
}
}
impl Default for #struct_name {
fn default() -> Self {
Self {
#( #field_initializers_default )*
}
}
}
};
output.into()
}