cgp-macro-lib 0.7.0

Context-generic programming core component macros implemented as a library.
Documentation
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::Plus;
use syn::{Generics, Ident, ItemFn, ItemImpl, TypeParamBound, parse_quote, parse2};

use crate::cgp_fn::{
    FunctionAttributes, ImplicitArgField, apply_use_type_attributes_to_item_impl,
    build_implicit_args_bounds,
};
use crate::cgp_impl::derive_provider_bounds;

pub fn derive_item_impl(
    trait_ident: &Ident,
    item_fn: &ItemFn,
    implicit_args: &[ImplicitArgField],
    generics: &Generics,
    attributes: &FunctionAttributes,
) -> syn::Result<ItemImpl> {
    let type_generics = generics.split_for_impl().1;

    let mut item_impl: ItemImpl = parse2(quote! {
        impl #trait_ident #type_generics for __Context__ {
            #item_fn
        }
    })?;

    item_impl.generics = generics.clone();
    item_impl
        .generics
        .params
        .insert(0, parse2(quote! { __Context__ })?);

    {
        let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();
        bounds.extend(attributes.extend.clone());

        for import in attributes.uses.iter() {
            bounds.push(parse2(quote! { #import })?);
        }

        if !bounds.is_empty() {
            item_impl
                .generics
                .make_where_clause()
                .predicates
                .push(parse2(quote! {
                    Self: #bounds
                })?);
        }
    }

    if !attributes.extend_where.is_empty() {
        item_impl
            .generics
            .make_where_clause()
            .predicates
            .extend(attributes.extend_where.clone());
    }

    if !implicit_args.is_empty() {
        let where_clause = item_impl.generics.make_where_clause();
        let bounds = build_implicit_args_bounds(implicit_args)?;

        where_clause.predicates.push(parse2(quote! {
            Self: #bounds
        })?);
    }

    if !attributes.use_type.is_empty() {
        item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?;
    }

    if !attributes.use_provider.is_empty() {
        let where_clause = item_impl.generics.make_where_clause();

        for spec in attributes.use_provider.iter() {
            let provider_bounds = derive_provider_bounds(&parse_quote! { Self }, spec)?;
            where_clause.predicates.push(provider_bounds);
        }
    }

    Ok(item_impl)
}