use convert_case::{Case, Casing}; use quote::{format_ident, quote}; 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)::* } } _ => todo!(), } } 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::>() } 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_combined_name = format_ident!( "{}{}ItemType", db_ident, field.0.to_string().to_case(Case::UpperCamel) ); let item_base_name = &field.0.to_string(); let item_type = &field.1; quote! { struct #item_combined_name; impl ::microrm::schema::DatabaseItem for #item_combined_name { fn item_key() -> &'static str { #item_base_name } fn dependency_keys() -> &'static [&'static str] { &[] } // TODO fn accept_entity_visitor(visitor: &mut impl ::microrm::schema::entity::EntityVisitor) { <#item_type as ::microrm::schema::DatabaseSpec>::accept_entity_visitor(visitor); } } v.visit::<#item_combined_name>(); } }); 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() }