cgp-macro-lib 0.7.0

Context-generic programming core component macros implemented as a library.
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Plus;
use syn::{Error, ItemImpl, TypeParamBound, parse_quote, parse2};

use crate::cgp_fn::{apply_use_type_attributes_to_item_impl, build_implicit_args_bounds};
use crate::cgp_impl::attributes::parse_impl_attributes;
use crate::cgp_impl::provider_bounds::derive_provider_bounds;
use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl, implicit_args};
use crate::derive_provider::{
    derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct,
};

pub fn derive_cgp_impl(
    spec: ImplProviderSpec,
    mut item_impl: ItemImpl,
) -> syn::Result<TokenStream> {
    let attributes = parse_impl_attributes(&mut item_impl.attrs)?;

    let implicit_args = implicit_args::extract_implicit_args_from_impl_items(&mut item_impl.items)?;

    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.uses.is_empty() {
        let mut bounds: Punctuated<TypeParamBound, Plus> = Punctuated::default();

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

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

    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);
        }
    }

    if spec.provider_type == parse_quote! { Self } {
        if item_impl.trait_.is_none() {
            return Err(Error::new(
                item_impl.span(),
                "Expected context type to be specified",
            ));
        }

        Ok(quote! {
            #item_impl
        })
    } else {
        let (_context_type, provider_impl) = derive_provider_impl(&spec.provider_type, item_impl)?;

        let component_type = match &spec.component_type {
            Some(component_type) => component_type.clone(),
            None => derive_component_name_from_provider_impl(&provider_impl)?,
        };

        let is_provider_for_impl: ItemImpl =
            derive_is_provider_for(&component_type, &provider_impl)?;

        let provider_struct = if spec.new_struct {
            Some(derive_provider_struct(&provider_impl)?)
        } else {
            None
        };

        Ok(quote! {
            #provider_struct

            #provider_impl

            #is_provider_for_impl
        })
    }
}