12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- 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::<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_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()
- }
|