cgp-macro-lib 0.7.0

Context-generic programming core component macros implemented as a library.
Documentation
use proc_macro2::Span;
use quote::{ToTokens, quote};
use syn::token::For;
use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse2};

use crate::parse::SimpleType;
use crate::replace_self::{
    replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident,
};

pub fn transform_impl_trait(
    item_impl: &ItemImpl,
    consumer_trait_path: &SimpleType,
    provider_type: &Type,
    context_type: &Type,
) -> syn::Result<ItemImpl> {
    let context_var = if let Ok(ident) = parse2::<Ident>(context_type.to_token_stream()) {
        to_snake_case_ident(&ident)
    } else {
        Ident::new("__context__", Span::call_site())
    };

    let local_assoc_types: Vec<Ident> = item_impl
        .items
        .iter()
        .filter_map(|item| {
            if let ImplItem::Type(assoc_type) = item {
                Some(assoc_type.ident.clone())
            } else {
                None
            }
        })
        .collect();

    let raw_out_impl = replace_self_type(
        item_impl.to_token_stream(),
        context_type.to_token_stream(),
        &local_assoc_types,
    );

    let mut out_impl: ItemImpl = parse2(raw_out_impl)?;
    out_impl.self_ty = Box::new(provider_type.clone());

    let mut provider_trait_path: SimpleType = consumer_trait_path.clone();

    match &mut provider_trait_path.generics {
        Some(generics) => {
            generics
                .args
                .insert(0, parse2(context_type.to_token_stream())?);
        }
        None => {
            provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?);
        }
    }

    out_impl.trait_ = Some((
        None,
        parse2(provider_trait_path.to_token_stream())?,
        For(Span::call_site()),
    ));

    for item in out_impl.items.iter_mut() {
        if let ImplItem::Fn(item_fn) = item
            && let Some(arg) = item_fn.sig.inputs.first_mut()
            && let FnArg::Receiver(receiver) = arg
        {
            *arg = replace_self_receiver(receiver, &context_var, context_type.to_token_stream());

            let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var);
            item_fn.block = parse2(replaced_block)?;
        }
    }

    Ok(out_impl)
}