cgp-macro-lib 0.6.1

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

use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{Fields, ItemImpl, ItemStruct, LitInt, parse_quote};

use crate::symbol::symbol_from_string;

pub fn derive_has_field_impls_from_struct(item_struct: &ItemStruct) -> Vec<ItemImpl> {
    let struct_ident = &item_struct.ident;

    let (impl_generics, ty_generics, where_clause) = item_struct.generics.split_for_impl();

    let mut item_impls = Vec::new();

    match &item_struct.fields {
        Fields::Named(fields) => {
            for field in fields.named.iter() {
                let field_ident = field.ident.as_ref().unwrap();

                let field_symbol = symbol_from_string(&field_ident.to_string());

                let field_type = &field.ty;

                let has_field_impl: ItemImpl = parse_quote! {
                    impl #impl_generics HasField< #field_symbol >
                        for #struct_ident #ty_generics
                    #where_clause
                    {
                        type Value = #field_type;

                        fn get_field(
                            &self,
                            key: ::core::marker::PhantomData< #field_symbol >,
                        ) -> &Self::Value
                        {
                            &self. #field_ident
                        }
                    }
                };

                let has_field_mut_impl: ItemImpl = parse_quote! {
                    impl #impl_generics HasFieldMut< #field_symbol >
                        for #struct_ident #ty_generics
                    #where_clause
                    {
                        fn get_field_mut(
                            &mut self,
                            key: ::core::marker::PhantomData< #field_symbol >,
                        ) -> &mut Self::Value
                        {
                            &mut self. #field_ident
                        }
                    }
                };

                item_impls.push(has_field_impl);
                item_impls.push(has_field_mut_impl);
            }
        }
        Fields::Unnamed(fields) => {
            for (i, field) in fields.unnamed.iter().enumerate() {
                let field_ident = LitInt::new(&format!("{i}"), field.span());
                let field_symbol = quote! { δ< #field_ident > };

                let field_type = &field.ty;

                let has_field_impl: ItemImpl = parse_quote! {
                    impl #impl_generics HasField< #field_symbol >
                        for #struct_ident #ty_generics
                    #where_clause
                    {
                        type Value = #field_type;

                        fn get_field(
                            &self,
                            key: ::core::marker::PhantomData< #field_symbol >,
                        ) -> &Self::Value
                        {
                            &self. #field_ident
                        }
                    }
                };

                let has_field_mut_impl: ItemImpl = parse_quote! {
                    impl #impl_generics HasFieldMut< #field_symbol >
                        for #struct_ident #ty_generics
                    #where_clause
                    {
                        fn get_field_mut(
                            &mut self,
                            key: ::core::marker::PhantomData< #field_symbol >,
                        ) -> &mut Self::Value
                        {
                            &mut self. #field_ident
                        }
                    }
                };

                item_impls.push(has_field_impl);
                item_impls.push(has_field_mut_impl);
            }
        }
        _ => {}
    }

    item_impls
}

pub fn derive_has_field(input: TokenStream) -> TokenStream {
    let item_struct: ItemStruct = syn::parse2(input).unwrap();

    let item_impls = derive_has_field_impls_from_struct(&item_struct);

    quote! {
        #( #item_impls )*
    }
}