cgp_macro_lib/
field.rs

1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::spanned::Spanned;
7use syn::{Fields, ItemImpl, ItemStruct, LitInt, parse_quote};
8
9use crate::symbol::symbol_from_string;
10
11pub fn derive_has_field_impls_from_struct(item_struct: &ItemStruct) -> Vec<ItemImpl> {
12    let struct_ident = &item_struct.ident;
13
14    let (impl_generics, ty_generics, where_clause) = item_struct.generics.split_for_impl();
15
16    let mut item_impls = Vec::new();
17
18    match &item_struct.fields {
19        Fields::Named(fields) => {
20            for field in fields.named.iter() {
21                let field_ident = field.ident.as_ref().unwrap();
22
23                let field_symbol = symbol_from_string(&field_ident.to_string());
24
25                let field_type = &field.ty;
26
27                let has_field_impl: ItemImpl = parse_quote! {
28                    impl #impl_generics HasField< #field_symbol >
29                        for #struct_ident #ty_generics
30                    #where_clause
31                    {
32                        type Value = #field_type;
33
34                        fn get_field(
35                            &self,
36                            key: ::core::marker::PhantomData< #field_symbol >,
37                        ) -> &Self::Value
38                        {
39                            &self. #field_ident
40                        }
41                    }
42                };
43
44                let has_field_mut_impl: ItemImpl = parse_quote! {
45                    impl #impl_generics HasFieldMut< #field_symbol >
46                        for #struct_ident #ty_generics
47                    #where_clause
48                    {
49                        fn get_field_mut(
50                            &mut self,
51                            key: ::core::marker::PhantomData< #field_symbol >,
52                        ) -> &mut Self::Value
53                        {
54                            &mut self. #field_ident
55                        }
56                    }
57                };
58
59                item_impls.push(has_field_impl);
60                item_impls.push(has_field_mut_impl);
61            }
62        }
63        Fields::Unnamed(fields) => {
64            for (i, field) in fields.unnamed.iter().enumerate() {
65                let field_ident = LitInt::new(&format!("{i}"), field.span());
66                let field_symbol = quote! { δ< #field_ident > };
67
68                let field_type = &field.ty;
69
70                let has_field_impl: ItemImpl = parse_quote! {
71                    impl #impl_generics HasField< #field_symbol >
72                        for #struct_ident #ty_generics
73                    #where_clause
74                    {
75                        type Value = #field_type;
76
77                        fn get_field(
78                            &self,
79                            key: ::core::marker::PhantomData< #field_symbol >,
80                        ) -> &Self::Value
81                        {
82                            &self. #field_ident
83                        }
84                    }
85                };
86
87                let has_field_mut_impl: ItemImpl = parse_quote! {
88                    impl #impl_generics HasFieldMut< #field_symbol >
89                        for #struct_ident #ty_generics
90                    #where_clause
91                    {
92                        fn get_field_mut(
93                            &mut self,
94                            key: ::core::marker::PhantomData< #field_symbol >,
95                        ) -> &mut Self::Value
96                        {
97                            &mut self. #field_ident
98                        }
99                    }
100                };
101
102                item_impls.push(has_field_impl);
103                item_impls.push(has_field_mut_impl);
104            }
105        }
106        _ => {}
107    }
108
109    item_impls
110}
111
112pub fn derive_has_field(input: TokenStream) -> TokenStream {
113    let item_struct: ItemStruct = syn::parse2(input).unwrap();
114
115    let item_impls = derive_has_field_impls_from_struct(&item_struct);
116
117    quote! {
118        #( #item_impls )*
119    }
120}