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::Comma;
use syn::{FieldValue, GenericArgument, Ident, ItemImpl, ItemStruct, Type, parse2};

use crate::derive_builder::{
    field_to_member, field_to_tag, field_value_expr, index_to_generic_ident, to_generic_args,
};

pub fn derive_update_field_impls(
    context_struct: &ItemStruct,
    builder_ident: &Ident,
) -> syn::Result<Vec<ItemImpl>> {
    let mut item_impls = Vec::new();

    let base_generic_args = to_generic_args(&context_struct.generics)?;

    for (current_index, current_field) in context_struct.fields.iter().enumerate() {
        let value_type = &current_field.ty;

        let mut generics = context_struct.generics.clone();
        let mut source_generic_args = base_generic_args.args.clone();
        let mut output_generic_args = base_generic_args.args.clone();
        let mut builder_fields = <Punctuated<FieldValue, Comma>>::new();

        for (other_index, other_field) in context_struct.fields.iter().enumerate() {
            let field_member = field_to_member(other_index, other_field);

            if other_index != current_index {
                let generic_param_name = index_to_generic_ident(other_index);

                generics.params.push(parse2(quote! {
                    #generic_param_name: MapType
                })?);

                let generic_arg: GenericArgument = parse2(quote! { #generic_param_name })?;
                source_generic_args.push(generic_arg.clone());
                output_generic_args.push(generic_arg);

                builder_fields.push(field_value_expr(
                    field_member.clone(),
                    quote! { self. #field_member },
                )?);
            } else {
                source_generic_args.push(parse2(quote! { __M1__ })?);
                output_generic_args.push(parse2(quote! { __M2__ })?);

                generics.params.push(parse2(quote! {
                    __M1__: MapType
                })?);

                generics.params.push(parse2(quote! {
                    __M2__: MapType
                })?);

                builder_fields.push(field_value_expr(field_member, quote! { value })?);
            }
        }

        let source_type: Type = parse2(quote! {
            #builder_ident < #source_generic_args >
        })?;

        let output_type: Type = parse2(quote! {
            #builder_ident < #output_generic_args >
        })?;

        let tag_type = field_to_tag(current_index, current_field)?;

        let (impl_generics, _, where_clause) = generics.split_for_impl();

        let member = field_to_member(current_index, current_field);

        let item_impl = parse2(quote! {
            impl #impl_generics UpdateField< #tag_type, __M2__ >
                for #source_type
            #where_clause
            {
                type Value = #value_type;

                type Mapper = __M1__;

                type Output = #output_type;

                fn update_field(
                    self,
                    _tag: ::core::marker::PhantomData< #tag_type >,
                    value: __M2__::Map<Self::Value>,
                ) -> (__M1__::Map<Self::Value>, Self::Output) {
                    (
                        self. #member,
                        #builder_ident {
                            #builder_fields
                        },
                    )
                }
            }
        })?;

        item_impls.push(item_impl);
    }

    Ok(item_impls)
}