mydi_macros 0.2.4

Macroses for MyDI library
Documentation
use syn::{parse_macro_input, DeriveInput, Error, Type};
use syn::{Field, Data, Fields, DataStruct, Generics};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote_spanned, quote};
use syn::{Result, Token};
use syn::parse::{ParseStream};
use quote::ToTokens;


pub(crate) fn derive_expand_impl(ident: Ident,
                                 data: Data,
                                 generics: Generics) -> syn::Result<TokenStream> {
    let fields: Vec<_> = match &data {
        Data::Struct(DataStruct { fields, .. }) => match fields {
            Fields::Named(named) => named.named.clone(),
            Fields::Unit => Default::default(),
            _ => {
                return Ok(quote_spanned! {
                    ident.span() => compile_error!("You can only derive Inject on structs with named fields or empty structs");
                });
            }
        },
        _ => {
            return Ok(quote_spanned! {
                ident.span() => compile_error!("You can only derive Inject on structs");
            });
        }
    }.into_iter()
        .collect();


    if fields.iter().any(|f| ignore_expand(f) && nested_expand(f)) {
        return Ok(quote_spanned! {
                    ident.span() => compile_error!("Can't ignore and expand field simultaneously");
                });
    }

    if fields.iter().any(|f| ignore_expand(f) && force_expand(f)) {
        return Ok(quote_spanned! {
                    ident.span() => compile_error!("Can't ignore and force expanding for field simultaneously");
                });
    }

    let fields_to_extract: Vec<TokenStream> = fields.iter()
        .filter(|x| !ignore_expand(x))
        .map(|field| {
            let ident = format_ident!("{}", field.ident.as_ref().unwrap());
            let name = quote!( #ident );
            name
        })
        .collect::<Vec<_>>();

    let fields_unnested: Vec<TokenStream> = fields.iter()
        .filter(|x| !ignore_expand(x) && !nested_expand(x) && !force_expand(x))
        .map(|field| {
            let ident = format_ident!("{}", field.ident.as_ref().unwrap());
            let name = quote!( #ident );
            name
        })
        .collect::<Vec<_>>();

    let forced_extraction: Vec<TokenStream> = fields.iter()
        .filter(|x| !ignore_expand(x) && force_expand(x))
        .map(|field| {
            let ident = format_ident!("{}", field.ident.as_ref().unwrap());
            let name = quote!( #ident );
            name
        })
        .collect::<Vec<_>>();

    let fields_nested: Vec<TokenStream> = fields.iter()
        .filter(|x| !ignore_expand(x) && nested_expand(x))
        .map(|field| {
            let ident = format_ident!("{}", field.ident.as_ref().unwrap());
            let name = quote!( #ident );
            name
        })
        .collect::<Vec<_>>();

    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    Ok(quote!(

        #[automatically_derived]
        impl #impl_generics mydi::expander::ComponentExpander for #ident #ty_generics #where_clause {
            fn expand<INJECTION_BINDER_TYPE: Clone + 'static>(self, injector: mydi::injection_binder::InjectionBinder<INJECTION_BINDER_TYPE>) -> mydi::injection_binder::InjectionBinder<INJECTION_BINDER_TYPE>  {
                let Self {#( #fields_to_extract),* , .. } = self;

                injector #(
                    .instance(#fields_unnested)
                )*
                #(
                    .instance(#forced_extraction.clone())
                )*
                #(
                    .expand(#fields_nested)
                )*
            }

        }

    ))
}

fn ignore_expand(field: &Field) -> bool {
    for attribute in &field.attrs {
        if attribute.path().is_ident("ignore_expand") ||
            attribute.path().is_ident("mydi::ignore_expand") {
            return true;
        }
    }

    false
}


fn nested_expand(field: &Field) -> bool {
    for attribute in &field.attrs {
        if attribute.path().is_ident("nested_expand") ||
            attribute.path().is_ident("mydi::nested_expand") {
            return true;
        }
    }

    false
}


fn force_expand(field: &Field) -> bool {
    for attribute in &field.attrs {
        if attribute.path().is_ident("force_expand") ||
            attribute.path().is_ident("mydi::force_expand") {
            return true;
        }
    }

    false
}