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, Span};
use quote::quote;
use syn::{FnArg, Pat, Receiver, TraitItemFn, Type, TypeReference};
use syn::__private::TokenStream2;
use syn::spanned::Spanned;
use syn::Type::Reference;

pub struct TraitFnMeta(TraitItemFn);


impl TraitFnMeta {
    pub const fn new(item_fn: TraitItemFn) -> Self {
        Self(item_fn)
    }

    pub fn has_immutable_self_ref_receiver(&self) -> bool {
        self.0.sig.receiver().map_or(false, |recv| {
            recv.reference.is_some() && recv.mutability.is_none()
        })
    }

    pub fn expand_fn(&mut self, delegatable_ident: &TokenStream2) -> syn::Result<TokenStream2> {
        let stmt = self.delegate_stmt(delegatable_ident)?;

        self.0.default.replace(syn::parse2(quote!({
            #stmt
        })).unwrap());

        let f = &self.0;
        Ok(quote::quote! {
            #[inline(always)]
            #f
        })
    }


    fn delegate_stmt(&self, delegatable_ident: &TokenStream2) -> syn::Result<TokenStream2> {
        if let Some(receiver) = self.0.sig.receiver() {
            self.expand_delegate_receiver(receiver)
        } else {
            self.expand_delegate_static(delegatable_ident)
        }
    }


    fn expand_delegate_static(&self, delegatable_ident: &TokenStream2) -> syn::Result<TokenStream2> {
        let stmt = self.call_stmt();
        Ok(quote! {
            <Self as #delegatable_ident>::A::#stmt
        })
    }

    fn expand_delegate_receiver(&self, receiver: &Receiver) -> syn::Result<TokenStream2> {
        let ty = *receiver.ty.clone();
        match ty {
            Type::Path(_) => Ok(self.delegate_owned()),
            Reference(ty_ref) => Ok(self.delegate_ref_or_mut(&ty_ref)),
            _ => Err(
                syn::Error::new(
                    Span::call_site(),
                    format!("expect type must be one of 'self', '&self', '&mut self' bad was {ty:?}"),
                )),
        }
    }

    fn delegate_owned(&self) -> TokenStream2 {
        let call = self.call();
        quote::quote! {
            let m = self.delegate_by_owned();
            #call
        }
    }


    fn delegate_ref_or_mut(&self, ty_ref: &TypeReference) -> TokenStream2 {
        let call = self.call();

        if ty_ref.mutability.is_none() {
            quote::quote! {
                let m = self.delegate_by_ref();
                #call
            }
        } else {
            quote::quote! {
                let m = self.delegate_by_mut();
                #call
            }
        }
    }


    fn call_stmt(&self) -> TokenStream2 {
        let inputs = self.expand_inputs();
        let fn_name = &self.0.sig.ident;
        let asyncness = self.0.sig.asyncness.map(|_| quote!(.await));
        if self.0.sig.generics.params.is_empty() {
            quote!(#fn_name(#inputs)#asyncness)
        } else {
            let (_, generics, _) = &self.0.sig.generics.split_for_impl();
            quote!(#fn_name::#generics(#inputs)#asyncness)
        }
    }

    fn call(&self) -> TokenStream2 {
        let stmt = self.call_stmt();
        let call = quote!(return t.#stmt;);
        quote! {
            if let Some(t) = m.0{
                #call
            }
            if let Some(t) = m.1{
                #call
            }
            if let Some(t) = m.2{
                #call
            }
            if let Some(t) = m.3{
                #call
            }
            if let Some(t) = m.4{
                #call
            }
            if let Some(t) = m.5{
                #call
            }
            if let Some(t) = m.6{
                #call
            }
            if let Some(t) = m.7{
                #call
            }
            if let Some(t) = m.8{
                #call
            }
            if let Some(t) = m.9{
                #call
            }
            if let Some(t) = m.10{
                #call
            }
            if let Some(t) = m.11{
                #call
            }
            panic!("unreachable");
        }
    }


    fn expand_inputs(&self) -> TokenStream2 {
        let inputs = self
            .0
            .sig
            .inputs
            .iter()
            .filter_map(|args| match args {
                FnArg::Typed(pat_type) => {
                    let ident = require_ident(&pat_type.pat).ok()?;
                    Some(quote::quote! {#ident,})
                }
                _ => None,
            });

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

fn require_ident(pat: &Pat) -> syn::Result<&Ident> {
    if let Pat::Ident(ident) = pat {
        Ok(&ident.ident)
    } else {
        Err(syn::Error::new(
            pat.span(),
            "Required PatIdent But Different",
        ))
    }
}