use quote::quote;
pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: syn::DeriveInput = syn::parse_macro_input!(tokens);
let items = if let syn::Data::Struct(syn::DataStruct {
struct_token: _,
fields: syn::Fields::Named(fields),
semi_token: _,
}) = input.data
{
fields
.named
.into_iter()
.map(|f| (f.ident.unwrap(), f.ty))
.collect::<Vec<_>>()
} else {
panic!("Can only derive Schema on data structs with named fields!");
};
let db_ident = input.ident;
let item_names = items
.iter()
.map(|field| field.0.clone())
.collect::<Vec<_>>();
let item_types = items
.iter()
.map(|field| field.1.clone())
.collect::<Vec<_>>();
let visit_items = items.iter().map(|field| {
let item_type = &field.1;
quote! {
<#item_type as ::microrm::schema::DatabaseItem>::accept_item_visitor(v);
}
});
let name_field = match input
.attrs
.iter()
.filter(|v| v.path.is_ident("name"))
.map(|v| v.parse_meta())
.next()
{
Some(Ok(syn::Meta::NameValue(nv))) => {
let literal = nv.lit;
quote! { const NAME: Option<&'static str> = Some(#literal); }
},
Some(_) => {
panic!("expected name format is name = \"...\"")
},
None => quote! {},
};
quote! {
impl ::microrm::schema::DatabaseItem for #db_ident {
fn accept_item_visitor(v: &mut impl ::microrm::schema::DatabaseItemVisitor) {
use ::microrm::schema::DatabaseItem;
#(#visit_items)*
}
fn build(seal: ::microrm::schema::BuildSeal) -> Self where Self: Sized {
Self {
#( #item_names: < #item_types as ::microrm::schema::DatabaseItem>::build(seal) ),*
}
}
type Subitems = ( #(#item_types),* , );
}
impl ::microrm::schema::Schema for #db_ident { #name_field }
}
.into()
}