microrm-macros 0.4.1

Procedural macro implementations for the microrm crate.
Documentation
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()
}