async-trait-ext 0.2.1

a procedural macro for async traits
Documentation
use crate::{
    input::TraitInput,
    methods::{expand::GenericsExpand, future_type},
};
use macro_compose::{Collector, Context, Expand};
use proc_macro2::Span;
use quote::format_ident;
use std::iter::FromIterator;
use syn::{
    parse_quote, punctuated::Punctuated, Expr, GenericParam, Index, ItemImpl, ItemTrait, Member,
    ReturnType, TraitItemMethod,
};

pub struct ImplFutureExpand<'a>(pub &'a ItemTrait);

impl Expand<TraitItemMethod> for ImplFutureExpand<'_> {
    type Output = ItemImpl;

    fn expand(&self, input: &TraitItemMethod, c: &mut Collector) -> Option<Self::Output> {
        input.sig.asyncness?;
        if input.default.is_some() {
            return None;
        }

        let attrs = TraitInput::from(self.0.attrs.as_slice());

        let mut ctx = Context::new_by_ref(c, input);
        let generics = ctx.capture(&GenericsExpand {
            item: self.0,
            default_lifetime: parse_quote!('__default_lifetime),
        })?;
        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

        let output = match &input.sig.output {
            ReturnType::Default => parse_quote!(()),
            ReturnType::Type(_, ty) => *ty.clone(),
        };

        let args = input.sig.inputs.iter().enumerate().map(|(idx, _)| {
            Member::Unnamed(Index {
                index: idx as u32,
                span: Span::call_site(),
            })
        });

        let (_, trait_ty_generics, _) = self.0.generics.split_for_impl();
        let trait_turbofish = trait_ty_generics.as_turbofish();

        let mut generics = input.sig.generics.clone();
        generics.params = Punctuated::from_iter(
            input
                .sig
                .generics
                .params
                .iter()
                .filter(|p| !matches!(p, GenericParam::Lifetime(_)))
                .cloned(),
        );
        if generics.params.is_empty() {
            generics.lt_token = None;
            generics.gt_token = None;
        }
        let (_, method_ty_generics, _) = generics.split_for_impl();
        let method_turbofish = method_ty_generics.as_turbofish();

        let trait_ident = &self.0.ident;
        let method_ident = format_ident!("poll_{}", input.sig.ident);

        let path: Expr = if attrs.dynamic.is_some() {
            parse_quote!(#trait_ident #trait_turbofish :: #method_ident #method_turbofish)
        } else {
            parse_quote!(<__Self as #trait_ident #trait_turbofish >:: #method_ident #method_turbofish)
        };

        let future_type = future_type(self.0, input);
        Some(parse_quote!(
            impl #impl_generics ::core::future::Future for #future_type #ty_generics #where_clause {
                type Output = #output;

                fn poll(mut self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll<Self::Output> {
                    let this = &mut *self;
                    #path ( #(this. #args .into(),)* cx)
                }
            }
        ))
    }
}