use quote::{quote, ToTokens};
fn type_to_expression_context_type(ty: &syn::Type) -> proc_macro2::TokenStream {
fn handle_path_segment(seg: &syn::PathSegment) -> proc_macro2::TokenStream {
let ident = &seg.ident;
let args = &seg.arguments;
match seg.arguments.is_empty() {
true => quote! { #ident },
false => quote! { #ident :: #args },
}
}
match ty {
syn::Type::Path(path) => {
let new_segments = path.path.segments.iter().map(handle_path_segment);
quote! {
#(#new_segments)::*
}
},
v => v.into_token_stream(),
}
}
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 Database on data structs with named fields!");
};
let db_ident = input.ident;
let visit_items = items.iter().map(|field| {
let item_type = &field.1;
quote! {
<#item_type as ::microrm::schema::DatabaseItem>::accept_item_visitor(v);
}
});
let build_method = items.iter().map(|field| {
let item_name = &field.0;
let item_type = type_to_expression_context_type(&field.1);
quote! {
#item_name : #item_type :: build(conn.clone())
}
});
quote! {
impl ::microrm::schema::Database for #db_ident {
fn build(conn: ::microrm::db::Connection) -> Self where Self: Sized {
Self { #(#build_method),* }
}
fn accept_item_visitor(v: &mut impl ::microrm::schema::DatabaseItemVisitor) {
#(#visit_items)*
}
}
}
.into()
}