auto-delegate-impl 0.1.2

Auto delegate allows you that automatic impl of traits and delegate their handling to child members.
Documentation
use proc_macro2::Ident;
use quote::quote;
use syn::__private::TokenStream2;
use syn::ItemEnum;

use crate::attribute::{find_to_attribute, syn_error_not_found_to_attribute, syn_error_not_found_trait_names, trait_names};
use crate::delegatable::delegatable_ident_with_generics;

pub fn try_expand_derive_enum(item_enum: &ItemEnum) -> syn::Result<TokenStream2> {
    let delegate_by_owned = expand_impl_delegate_by_owned(item_enum);
    let delegate_by_ref = expand_impl_delegate_by_ref(item_enum);
    let delegate_by_mut = expand_impl_delegate_by_mut(item_enum);
    let to_attr = find_to_attribute(&item_enum.attrs).ok_or(syn_error_not_found_to_attribute())?;
    let trait_names = trait_names(&to_attr).ok_or(syn_error_not_found_trait_names())?;
    let expand_impls = trait_names
        .into_iter()
        .map(|trait_name| {
            expand_impl_delegatable(trait_name, item_enum, &delegate_by_owned, &delegate_by_ref, &delegate_by_mut)
        });

    Ok(quote::quote! {
        #(#expand_impls)*
    })
}


fn expand_impl_delegatable(
    trait_name: Ident,
    item_enum: &ItemEnum,
    delegate_by_owned: &TokenStream2,
    delegate_by_ref: &TokenStream2,
    delegate_by_mut: &TokenStream2,
) -> TokenStream2 {
    let marker_ident = delegatable_ident_with_generics(trait_name.clone());
    let enum_name = &item_enum.ident;
    let generics = &item_enum.generics;
    let (_, types, _) = generics.split_for_impl();

    let bounds = &generics.where_clause;
    let (a, b, c, d, e, f, g, h, i, j, k, l) = variant_types(item_enum);

    quote::quote! {
        impl #generics #marker_ident for #enum_name #types
            #bounds
        {
            type A = #a;
            type B = #b;
            type C = #c;
            type D = #d;
            type E = #e;
            type F = #f;
            type G = #g;
            type H = #h;
            type I = #i;
            type J = #j;
            type K = #k;
            type L = #l;

            #delegate_by_owned
            #delegate_by_ref
            #delegate_by_mut
        }
    }
}


fn expand_impl_delegate_by_owned(item_enum: &ItemEnum) -> TokenStream2 {
    let patterns = pattern_match_fields(item_enum);

    quote::quote! {
        #[inline(always)]
        fn delegate_by_owned(self) -> auto_delegate::Delegates<Self::A, Self::B, Self::C, Self::D, Self::E, Self::F, Self::G, Self::H, Self::I, Self::J, Self::K, Self::L>{
            match self{
                 #patterns
            }
        }
    }
}


fn expand_impl_delegate_by_ref(item_enum: &ItemEnum) -> TokenStream2 {
    let patterns = pattern_match_fields(item_enum);

    quote::quote! {
        #[inline(always)]
        fn delegate_by_ref(&self) -> auto_delegate::Delegates<&Self::A, &Self::B, &Self::C, &Self::D, &Self::E, &Self::F, &Self::G, &Self::H, &Self::I, &Self::J, &Self::K, &Self::L>{
            match self{
                 #patterns
            }
        }
    }
}


fn expand_impl_delegate_by_mut(item_enum: &ItemEnum) -> TokenStream2 {
    let patterns = pattern_match_fields(item_enum);

    quote::quote! {
        #[inline(always)]
        fn delegate_by_mut(&mut self) -> auto_delegate::Delegates<&mut Self::A, &mut Self::B, &mut Self::C, &mut Self::D, &mut Self::E, &mut Self::F, &mut Self::G, &mut Self::H, &mut Self::I, &mut Self::J, &mut Self::K, &mut Self::L>{
            match self{
                 #patterns
            }
        }
    }
}


fn variant_types(item_enum: &ItemEnum) -> (TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2, TokenStream2) {
    let mut types = item_enum
        .variants
        .iter()
        .map(|v| v.fields.iter().next().unwrap().ty.clone())
        .map(|t| quote!(#t));

    let a = types.next().unwrap();
    let a2 = a.clone();
    let mut t = || {
        types.next().unwrap_or(a2.clone())
    };
    (a, t(), t(), t(), t(), t(), t(), t(), t(), t(), t(), t())
}


fn pattern_match_fields(item_enum: &ItemEnum) -> TokenStream2 {
    let matches = item_enum
        .variants
        .iter()
        .enumerate()
        .map(|(i, v)| {
            let ident = &v.ident;

            match i {
                0 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(Some(v), None, None, None, None, None, None, None, None, None, None, None),
                },
                1 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, Some(v), None, None, None, None, None, None, None, None, None, None),
                },
                2 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, Some(v),  None, None, None, None, None, None, None, None, None),
                },
                3 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, Some(v), None, None, None, None, None, None, None, None),
                },
                4 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, Some(v), None, None, None, None, None, None, None),
                },
                5 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, Some(v), None, None, None, None, None, None),
                },
                6 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, Some(v), None, None, None, None, None),
                },
                7 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, None, Some(v), None, None, None, None),
                },
                8 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, None, None, Some(v), None, None, None),
                },
                9 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, None, None, None, Some(v), None, None),
                },
                10 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, None, None, None, None, Some(v), None),
                },
                11 => quote::quote! {
                    Self::#ident(v) => auto_delegate::Delegates(None, None, None, None, None, None, None, None, None, None, None, Some(v)),
                },
                _ => panic!("not reachable")
            }
        });

    quote::quote! {
        #(#matches)*
    }
}