cgp-macro-lib 0.7.0

Context-generic programming core component macros implemented as a library.
Documentation
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;

use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{ImplItem, ImplItemType, ItemImpl, Path, Type, parse2};

use crate::delegate_components::define_struct;
use crate::delegate_components::merge_generics::merge_generics;
use crate::parse::{DelegateEntry, DelegateKey, DelegateMode, DelegateValue, ImplGenerics};

pub fn impl_delegate_components<T>(
    target_type: &Type,
    target_generics: &ImplGenerics,
    delegate_entries: &Punctuated<DelegateEntry<T>, Comma>,
) -> syn::Result<TokenStream>
where
    T: ToTokens,
{
    let mut out = TokenStream::new();

    for entry in delegate_entries.iter() {
        let source = &entry.value;
        for component in entry.keys.iter() {
            let impls = impl_delegate_component(
                target_type,
                target_generics,
                component,
                source,
                &entry.mode,
            )?;

            out.extend(impls);
        }

        if let DelegateValue::New(value) = source {
            let struct_ident = &value.struct_ident;

            let item_struct = define_struct(struct_ident, &value.struct_generics)?;

            let (impl_generics, type_generics, _) = value.struct_generics.split_for_impl();

            let target_type: Type = parse2(quote! { #struct_ident #type_generics })?;

            let impl_generics = parse2(quote! { #impl_generics })?;

            let inner_impls =
                impl_delegate_components(&target_type, &impl_generics, &value.entries)?;

            out.extend(item_struct.to_token_stream());
            out.extend(inner_impls);
        }
    }

    Ok(out)
}

pub fn impl_delegate_component<T>(
    target_type: &Type,
    target_generics: &ImplGenerics,
    component: &DelegateKey<T>,
    value: &DelegateValue,
    mode: &DelegateMode,
) -> syn::Result<TokenStream>
where
    T: ToTokens,
{
    let component_type = &component.ty;

    let delegate_target_type = match mode {
        DelegateMode::Provider(_) => value.as_type(),
        DelegateMode::Direct(_) => {
            let value_type = value.as_type();
            parse2(quote! {
                < #value_type as DelegateComponent< #component_type > >::Delegate
            })?
        }
    };

    let delegate_trait_path: Path = parse2(quote!(DelegateComponent < #component_type >))?;

    let delegate_type: ImplItemType = parse2(quote!(type Delegate = #delegate_target_type;))?;

    let mut delegate_generics =
        merge_generics(&target_generics.generics, &component.generics.generics);

    if mode.is_direct() {
        let value_type = value.as_type();
        let where_clause = delegate_generics.make_where_clause();

        where_clause.predicates.push(parse2(
            quote!( #value_type : DelegateComponent< #component_type > ),
        )?);
    }

    let is_provider_generics = {
        let mut generics = delegate_generics.clone();

        generics.params.push(parse2(quote!(__Context__))?);
        generics.params.push(parse2(quote!(__Params__))?);

        let where_clause = generics.make_where_clause();

        where_clause.predicates.push(parse2(
            quote!( #delegate_target_type : IsProviderFor< #component_type, __Context__, __Params__ > ),
        )?);

        generics
    };

    let delegate_impl = ItemImpl {
        attrs: Vec::new(),
        defaultness: None,
        unsafety: None,
        impl_token: Default::default(),
        generics: delegate_generics,
        trait_: Some((None, delegate_trait_path, Default::default())),
        self_ty: Box::new(target_type.clone()),
        brace_token: Default::default(),
        items: vec![ImplItem::Type(delegate_type)],
    };

    let is_provider_trait_path: Path =
        parse2(quote!( IsProviderFor< #component_type, __Context__, __Params__ > ))?;

    let is_provider_impl = ItemImpl {
        attrs: Vec::new(),
        defaultness: None,
        unsafety: None,
        impl_token: Default::default(),
        generics: is_provider_generics,
        trait_: Some((None, is_provider_trait_path, Default::default())),
        self_ty: Box::new(target_type.clone()),
        brace_token: Default::default(),
        items: Default::default(),
    };

    let out = quote! {
        #delegate_impl
        #is_provider_impl
    };

    Ok(out)
}