reflectix_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3
4#[derive(std::hash::Hash, Clone, PartialEq, Eq)]
5enum FieldId {
6    Named(syn::Ident),
7    Index(syn::LitInt),
8}
9
10impl FieldId {
11    pub fn as_named(&self) -> &syn::Ident {
12        match &self {
13            Self::Named(name) => name,
14            _ => panic!("Not a named ID"),
15        }
16    }
17    pub fn as_indexed(&self) -> &syn::LitInt {
18        match &self {
19            Self::Index(name) => name,
20            _ => panic!("Not a indexed ID"),
21        }
22    }
23}
24
25struct Field {
26    id: FieldId,
27    ty_ident: syn::Ident,
28}
29
30enum Fields {
31    Named(Vec<Field>),
32    Indexed(Vec<Field>),
33    Unit,
34}
35
36struct FieldsIter<'a> {
37    iter: std::option::Option<std::slice::Iter<'a, Field>>,
38}
39
40impl<'a> Iterator for FieldsIter<'a> {
41    type Item = &'a Field;
42
43    fn next(&mut self) -> Option<Self::Item> {
44        match self.iter {
45            Some(ref mut iter) => iter.next(),
46            None => None,
47        }
48    }
49}
50
51impl<'a> DoubleEndedIterator for FieldsIter<'a> {
52    fn next_back(&mut self) -> Option<Self::Item> {
53        match self.iter {
54            Some(ref mut iter) => iter.next_back(),
55            None => None,
56        }
57    }
58}
59
60impl<'a> ExactSizeIterator for FieldsIter<'a> {
61    fn len(&self) -> usize {
62        match &self.iter {
63            Some(iter) => iter.len(),
64            None => 0usize,
65        }
66    }
67}
68
69impl Fields {
70    fn iter<'a>(&'a self) -> FieldsIter<'a> {
71        match self {
72            Fields::Named(named) => FieldsIter {
73                iter: Some(named.iter()),
74            },
75            Fields::Indexed(indexed) => FieldsIter {
76                iter: Some(indexed.iter()),
77            },
78            Fields::Unit => FieldsIter { iter: None },
79        }
80    }
81}
82
83struct Variant {
84    name: syn::Ident,
85    discriminator: syn::LitInt,
86    fields: Fields,
87}
88
89struct Variants {
90    variants: Vec<Variant>,
91}
92
93enum Data {
94    Struct(Fields),
95    Enum(Variants),
96}
97
98fn create_meta_fields<'a, I: Iterator<Item = &'a syn::Field>>(fields: I) -> Fields {
99    let mut new_fields = Vec::new();
100    for (index, field) in fields.enumerate() {
101        let field_id = match field.ident.as_ref() {
102            Some(str_id) => FieldId::Named(str_id.clone()),
103            None => FieldId::Index(syn::LitInt::new(
104                &index.to_string(),
105                proc_macro2::Span::call_site(),
106            )),
107        };
108
109        let syn::Type::Path(ref type_ident) = field.ty else {
110            panic!("Unsupported field type used in ",)
111        };
112
113        let Some(type_ident) = type_ident.path.get_ident() else {
114            todo!();
115        };
116
117        new_fields.push(Field {
118            id: field_id,
119            ty_ident: type_ident.clone(),
120        });
121    }
122
123    match new_fields.first() {
124        Some(field) => match field.id {
125            FieldId::Named(_) => Fields::Named(new_fields),
126            FieldId::Index(_) => Fields::Indexed(new_fields),
127        },
128        None => Fields::Unit,
129    }
130}
131
132fn create_meta_variants<'a, I: Iterator<Item = &'a syn::Variant>>(variants: I) -> Variants {
133    let mut new_variants = Vec::new();
134
135    for (index, variant) in variants.enumerate() {
136        let variant_name = variant.ident.clone();
137        let fields = create_meta_fields(variant.fields.iter());
138
139        new_variants.push(Variant {
140            discriminator: syn::LitInt::new(&index.to_string(), variant_name.span()),
141            name: variant_name,
142            fields,
143        })
144    }
145
146    Variants {
147        variants: new_variants,
148    }
149}
150
151struct MetaType {
152    ident: syn::Ident,
153    info_ident: syn::Ident,
154    data: Data,
155}
156
157impl MetaType {
158    pub fn new(input: &syn::DeriveInput) -> Self {
159        let ident = input.ident.clone();
160
161        let meta_data = match &input.data {
162            syn::Data::Struct(syn::DataStruct { fields, .. }) => {
163                let fields_iter = match fields {
164                    syn::Fields::Named(named) => create_meta_fields(named.named.iter()),
165                    syn::Fields::Unnamed(unnamed) => create_meta_fields(unnamed.unnamed.iter()),
166                    syn::Fields::Unit => Fields::Unit,
167                };
168                Data::Struct(fields_iter)
169            }
170            syn::Data::Enum(enum_data) => {
171                Data::Enum(create_meta_variants(enum_data.variants.iter()))
172            }
173            syn::Data::Union(_) => panic!("Unions are not supported"),
174        };
175
176        let info_ident = format_ident!("{}_TYPE_INFO", ident.to_string().to_ascii_uppercase());
177
178        Self {
179            ident,
180            data: meta_data,
181            info_ident,
182        }
183    }
184}
185
186mod gen {
187
188    use quote::format_ident;
189    use quote::quote;
190    use quote::quote_spanned;
191    use quote::ToTokens;
192
193    use super::FieldId;
194    use crate::Variants;
195
196    use super::Fields;
197    use super::MetaType;
198
199    use std::collections::HashMap;
200
201    fn collect_fields(fields: &Fields) -> proc_macro2::TokenStream {
202        match fields {
203            Fields::Named(named) => {
204                let mut fields_definition = Vec::new();
205                for field in named.iter() {
206                    let crate::FieldId::Named(ref ident) = field.id else {
207                        unreachable!()
208                    };
209                    let name = ident.to_string();
210                    let type_ident = field.ty_ident.clone();
211
212                    fields_definition.push(quote! {
213                        reflectix_core::Field {
214                            id: reflectix_core::FieldId::Named(#name),
215                            ty: <#type_ident as reflectix_core::TypeInfo>::INFO,
216                        }
217                    });
218                }
219
220                quote! {
221                    reflectix_core::Fields::Named(&[#(#fields_definition),*])
222
223                }
224            }
225            Fields::Indexed(unnamed) => {
226                let mut fields_definition = Vec::new();
227                for field in unnamed.iter() {
228                    let FieldId::Index(ref ident) = field.id else {
229                        unreachable!()
230                    };
231                    let type_ident = field.ty_ident.clone();
232
233                    fields_definition.push(quote! {
234                        reflectix_core::Field {
235                            id: reflectix_core::FieldId::Indexed(#ident),
236                            ty: <#type_ident as reflectix_core::TypeInfo>::INFO,
237                        }
238                    });
239                }
240
241                quote! {
242                    reflectix_core::Fields::Indexed(&[#(#fields_definition),*])
243
244                }
245            }
246            Fields::Unit => quote! {reflectix_core::Fields::Unit},
247        }
248    }
249
250    fn collect_variants(variants: &Variants) -> proc_macro2::TokenStream {
251        let mut variants_list = Vec::new();
252
253        for (discriminator, variant) in variants.variants.iter().enumerate() {
254            let variant_name = variant.name.to_string();
255            let fields_stmt = collect_fields(&variant.fields);
256
257            variants_list.push(quote! {
258                reflectix_core::Variant {
259                    ident: #variant_name,
260                    fields: #fields_stmt,
261                    discriminator: #discriminator
262                }
263            });
264        }
265
266        quote! {
267            reflectix_core::Variants{variants: &[#(#variants_list),*]}
268        }
269    }
270
271    pub fn create_const_definition(meta: &MetaType) -> proc_macro2::TokenStream {
272        let data_definition = match &meta.data {
273            crate::Data::Struct(fields) => {
274                let fields = collect_fields(&fields);
275                quote! {
276                    reflectix_core::Data::Struct(#fields)
277                }
278            }
279            crate::Data::Enum(variants) => {
280                let variants = collect_variants(&variants);
281                quote! {
282                    reflectix_core::Data::Enum(#variants)
283                }
284            }
285        };
286
287        let const_ident = &meta.info_ident;
288        let ty_ident = meta.ident.to_string();
289
290        let const_type_info_stmt = quote_spanned! {proc_macro2::Span::mixed_site()=>
291          const #const_ident: reflectix_core::Type = reflectix_core::Type {
292              ident: #ty_ident,
293              data: #data_definition,
294          };
295        };
296        const_type_info_stmt
297    }
298
299    fn field_id_to_tokens(id: &FieldId) -> proc_macro2::TokenStream {
300        match id {
301            FieldId::Named(ident) => {
302                let as_str = ident.to_string();
303                quote! {
304                    reflectix_core::FieldId::Named(#as_str)
305                }
306            }
307            FieldId::Index(index) => quote! {
308                reflectix_core::FieldId::Index(#index)
309            },
310        }
311    }
312
313    fn create_dyn_field_access_match(
314        self_ident: Option<&syn::Ident>,
315        input_id_ident: &syn::Ident,
316        fields: &Fields,
317        is_mut_ref: bool,
318        is_accessing_tuple_enum_variant: bool,
319    ) -> proc_macro2::TokenStream {
320        let ref_producer = |ident: &syn::Ident| {
321            let field_ident = match self_ident {
322                Some(self_ident) => quote! {#self_ident.#ident},
323                None => ident.to_token_stream(),
324            };
325
326            // match is_mut_ref {
327            //     true => {
328            //         quote! {&mut #field_ident}
329            //     }
330            //     false => {
331            //         quote! {& #field_ident}
332            //     }
333            // }
334            field_ident
335        };
336
337        let mut patterns = Vec::new();
338        let mut arms = Vec::new();
339
340        for field in fields.iter() {
341            let field_id_as_tokens = field_id_to_tokens(&field.id);
342
343            let attr_access_name = match &field.id {
344                FieldId::Named(ident) => ident.clone(),
345                FieldId::Index(index) => {
346                    // tuple-emum field names are prefixed with _ to make them valid idents
347                    let prefix = if is_accessing_tuple_enum_variant {
348                        "_"
349                    } else {
350                        ""
351                    }
352                    .to_string();
353                    syn::Ident::new(&(prefix + &index.clone().to_string()), index.span())
354                }
355            };
356
357            let pattern = quote! {
358                #field_id_as_tokens
359            };
360            let mut field_ref = ref_producer(&attr_access_name);
361
362            let field_ty_ident = &field.ty_ident;
363
364            // need to extend lifetime
365            //
366            // SAFETY: this field is part of enum and we are accessing it in correct variant
367            // therefore, it is safe to extend this reference live to &self lifetime
368            // as this field will live for as long, as enum is living
369
370            if !is_accessing_tuple_enum_variant {
371                let ref_type = match is_mut_ref {
372                    true => quote! {&mut },
373                    false => quote!(&),
374                };
375                field_ref = quote! {#ref_type #field_ref};
376            }
377
378            let caster_block = match is_mut_ref {
379                true => quote! {
380                    let field_ref = (#field_ref as *mut #field_ty_ident) as *mut ();
381                    let target_id = std::any::TypeId::of::<#field_ty_ident>();
382
383                    return Ok(reflectix_core::UnsizeableMut::new(field_ref, target_id));
384                },
385                false => quote! {
386                    let field_ref = (#field_ref as *const #field_ty_ident) as *const ();
387                    let target_id = std::any::TypeId::of::<#field_ty_ident>();
388
389                    return Ok(reflectix_core::Unsizeable::new(field_ref, target_id));
390                },
391            };
392
393            patterns.push(pattern);
394            arms.push(caster_block);
395        }
396
397        // can avoid handling `Fields::Unit` variant because iterator will be empty
398        // and wildcard arm will be triggered
399        quote! {
400            match #input_id_ident {
401                #(#patterns => {#arms})*
402                _ => {
403                    return Err(reflectix_core::FieldAccessError::NotFound);
404                }
405            }
406        }
407    }
408
409    fn create_dyn_variant_access_match(
410        self_ident: &syn::Ident,
411        input_id_ident: &syn::Ident,
412        variants: &Variants,
413        is_mut_ref: bool,
414    ) -> proc_macro2::TokenStream {
415        let mut patterns = Vec::new();
416        let mut arms = Vec::new();
417
418        let inplace_ref_type = match is_mut_ref {
419            true => quote! {ref mut },
420            false => quote! {ref},
421        };
422
423        for variant in variants.variants.iter() {
424            let variant_name = &variant.name;
425
426            let pattern = match &variant.fields {
427                Fields::Named(named) => {
428                    let all_fields_idents = named
429                        .iter()
430                        .map(|x| x.id.as_named().clone())
431                        .collect::<Vec<_>>();
432
433                    quote! {
434                        Self::#variant_name{#(#inplace_ref_type #all_fields_idents),*}
435                    }
436                }
437                Fields::Indexed(indexed) => {
438                    let all_fields_idents = indexed
439                        .iter()
440                        // prefixing enum fields indexes with underscore to make them valid idents
441                        .map(|x| {
442                            syn::Ident::new(
443                                &format!("_{}", x.id.as_indexed().to_string()),
444                                x.ty_ident.span(),
445                            )
446                        })
447                        .collect::<Vec<_>>();
448
449                    match is_mut_ref {
450                        true => quote! {
451                            Self::#variant_name(#(ref mut #all_fields_idents),*)
452                        },
453                        false => quote! {
454                            Self::#variant_name(#(ref #all_fields_idents),*)
455                        },
456                    }
457                }
458                Fields::Unit => quote! {Self::#variant_name},
459            };
460
461            let arm = match &variant.fields {
462                iterable_fields @ (Fields::Named(_) | Fields::Indexed(_)) => {
463                    create_dyn_field_access_match(
464                        None,
465                        &input_id_ident,
466                        iterable_fields,
467                        is_mut_ref,
468                        true,
469                    )
470                }
471                Fields::Unit => quote! {
472                    return Err(reflectix_core::FieldAccessError::Unit);
473                },
474            };
475
476            patterns.push(pattern);
477            arms.push(arm);
478        }
479
480        quote! {
481            match #self_ident {
482                #(#patterns => {#arms})*
483                _ => {
484                    return Err(reflectix_core::FieldAccessError::UnmatchingDiscriminator);
485                }
486            }
487        }
488    }
489
490    // fn field<'s>(&'s self, id: FieldId) -> Result<&'s dyn Any, FieldAccessError>
491    pub fn create_get_dyn_field_method_body(
492        meta: &MetaType,
493        is_mut: bool,
494    ) -> proc_macro2::TokenStream {
495        let id_ident = syn::Ident::new("id", proc_macro2::Span::call_site());
496        let self_ident = syn::Ident::new("self", proc_macro2::Span::call_site());
497
498        let body = match meta.data {
499            crate::Data::Struct(ref fields) => {
500                create_dyn_field_access_match(Some(&self_ident), &id_ident, fields, is_mut, false)
501            }
502            crate::Data::Enum(ref variants) => {
503                create_dyn_variant_access_match(&self_ident, &id_ident, variants, is_mut)
504            }
505        };
506
507        body
508    }
509
510    /*
511    Generates match statement, which compares passed FieldId to "available" FieldId's
512
513    Downcasts field initializers from Box<dyn _> to concrete type of which field
514    Then, it takes out value directly from Box using some unsafe code
515
516    Finally, if every type matches those of fields (note: that fields of same type are supported, they just can't be named)
517    it constructs implementing type and returns it boxed with erased type
518
519    It returns erased type because if we already can refer to concrete type, then why to use reflective constructor in first place?
520    */
521    fn create_dyn_fields_ctor_body(
522        type_ident: &proc_macro2::TokenStream,
523        args_ident: &syn::Ident,
524        fields: &Fields,
525    ) -> proc_macro2::TokenStream {
526        match fields {
527            fields @ (Fields::Named(..) | Fields::Indexed(..)) => {
528                let mut field_downcast_stmts = Vec::new();
529                let mut field_identifiers = HashMap::new();
530                for (index, field) in fields.iter().enumerate().rev() {
531                    let curr_box_ident = format_ident!("boxed_{}", { index });
532
533                    let current_type = field.ty_ident.clone();
534                    let current_type_str = format!("{}", current_type);
535
536                    let downcast_stmt = quote! {
537                        let #curr_box_ident = #args_ident.pop().ok_or(reflectix_core::RuntimeConstructError::NotEnoughArgs)?;
538                        let #curr_box_ident = #curr_box_ident.downcast::<#current_type>().map_err(|_| reflectix_core::RuntimeConstructError::UnexpectedType{index: #index, expected: #current_type_str})?;
539
540                        let #curr_box_ident = unsafe {
541                            let as_raw = Box::into_raw(#curr_box_ident);
542                            let new_item = std::ptr::read(as_raw);
543                            std::mem::drop(Box::from_raw(as_raw));
544                            new_item
545                        };
546                    };
547
548                    field_downcast_stmts.push(downcast_stmt);
549                    field_identifiers.insert(field.id.clone(), curr_box_ident);
550                }
551
552                let is_indexed = fields
553                    .iter()
554                    .all(|x| matches!(x.id, crate::FieldId::Index(_)));
555
556                match is_indexed {
557                    true => {
558                        let keys = field_identifiers
559                            .keys()
560                            .map(FieldId::as_indexed)
561                            .collect::<Vec<_>>();
562                        quote! {
563                            #(#field_downcast_stmts)*
564
565                            return Ok(Box::new(#type_ident(#(#keys),*)));
566                        }
567                    }
568                    false => {
569                        let mut keys = Vec::new();
570                        let mut values = Vec::new();
571
572                        for (key, value) in field_identifiers.drain() {
573                            values.push(value);
574
575                            let crate::FieldId::Named(key) = key else {
576                                unreachable!()
577                            };
578                            let key =
579                                syn::Ident::new(&key.to_string(), proc_macro2::Span::call_site());
580                            keys.push(key);
581                        }
582
583                        quote! {
584                            #(#field_downcast_stmts)*
585
586                            return Ok(Box::new(#type_ident{#(#keys: #values),*}));
587                        }
588                    }
589                }
590            }
591            Fields::Unit => quote! {
592                return Ok(Box::new(#type_ident));
593            },
594        }
595    }
596
597    // fn construct_enum(
598    //         &self,
599    //         variant: &'static str,
600    //         args: Vec<Box<dyn Any>>,
601    //     ) -> Result<Box<dyn Any>, RuntimeConstructError>;
602    pub fn create_dyn_enum_ctor(meta: &MetaType) -> proc_macro2::TokenStream {
603        let args_ident = syn::Ident::new("args", proc_macro2::Span::call_site());
604        let requested_variant_ident = syn::Ident::new("variant", proc_macro2::Span::call_site());
605        let self_ty_ident = syn::Ident::new("Self", proc_macro2::Span::call_site());
606
607        let body = match &meta.data {
608            crate::Data::Struct(_) => quote! {
609                return Err(reflectix_core::RuntimeConstructError::NotEnum);
610            },
611            crate::Data::Enum(variants) => {
612                let mut patterns = Vec::new();
613                let mut bodies = Vec::new();
614                for variant in variants.variants.iter() {
615                    let variant_name_ident = &variant.name;
616                    let ctor_body = match &variant.fields {
617                        fields @ (Fields::Named(_) | Fields::Indexed(_)) => {
618                            let variant_ty_ident = quote! {#self_ty_ident::#variant_name_ident};
619                            create_dyn_fields_ctor_body(&variant_ty_ident, &args_ident, &fields)
620                        }
621                        Fields::Unit => quote! {
622                            return Ok(Box::new(#self_ty_ident::#variant_name_ident));
623                        },
624                    };
625                    let variant_name_str = variant.name.to_string();
626                    let pattern = quote! {
627                         #variant_name_str
628                    };
629                    patterns.push(pattern);
630                    bodies.push(ctor_body);
631                }
632
633                let match_stmt = quote! {
634                    match #requested_variant_ident {
635                        #(#patterns => {
636                            #bodies
637                        })*
638
639                        _ => {
640                            return Err(reflectix_core::RuntimeConstructError::InvalidVariant);
641                        }
642                    }
643                };
644
645                match_stmt
646            }
647        };
648
649        quote! {
650            fn construct_enum(
651                &self,
652                #requested_variant_ident: &'static str,
653                mut #args_ident: Vec<Box<dyn std::any::Any>>,
654            ) -> Result<Box<dyn std::any::Any>, reflectix_core::RuntimeConstructError> {
655                #body
656            }
657
658        }
659    }
660
661    // fn construct_struct(
662    //     &self,
663    //     args: Vec<Box<dyn Any>>,
664    // ) -> Result<Box<dyn Any>, RuntimeConstructError>;
665    pub fn create_dyn_struct_ctor(meta: &MetaType) -> proc_macro2::TokenStream {
666        let args_ident = syn::Ident::new("args", proc_macro2::Span::call_site());
667        let self_ty_ident = syn::Ident::new("Self", proc_macro2::Span::call_site());
668
669        let body = match &meta.data {
670            crate::Data::Struct(fields) => {
671                create_dyn_fields_ctor_body(&self_ty_ident.to_token_stream(), &args_ident, &fields)
672            }
673            crate::Data::Enum(_) => {
674                quote! {
675                    return Err(reflectix_core::RuntimeConstructError::NotStruct);
676                }
677            }
678        };
679
680        quote! {
681            fn construct_struct(
682                &self,
683                mut #args_ident: Vec<Box<dyn std::any::Any>>,
684            ) -> Result<Box<dyn std::any::Any>, reflectix_core::RuntimeConstructError> {
685                #body
686            }
687
688        }
689    }
690}
691
692#[proc_macro_derive(TypeInfo)]
693pub fn type_info_derive(input: TokenStream) -> TokenStream {
694    let ast: syn::DeriveInput = syn::parse(input).unwrap();
695
696    if !ast.generics.params.is_empty() {
697        panic!("Type info for generic struct is currently not supported");
698    }
699
700    let meta = MetaType::new(&ast);
701
702    let const_definition = gen::create_const_definition(&meta);
703
704    let const_def_ident = meta.info_ident.clone();
705    let ty_ident = meta.ident.clone();
706
707    let struct_ctor = gen::create_dyn_struct_ctor(&meta);
708    let enum_ctor = gen::create_dyn_enum_ctor(&meta);
709
710    let mut_field_access_body = gen::create_get_dyn_field_method_body(&meta, true);
711    let field_access_body = gen::create_get_dyn_field_method_body(&meta, false);
712    let tokens = quote! {
713        #const_definition
714
715        impl reflectix_core::TypeInfoDynamic for #ty_ident {
716             fn get_dynamic(&self) -> &'static reflectix_core::Type {
717                 &#const_def_ident
718             }
719
720             #struct_ctor
721             #enum_ctor
722
723            fn field<'s>(&'s self, id: reflectix_core::FieldId) -> Result<reflectix_core::Unsizeable<'s>, reflectix_core::FieldAccessError> {
724                #field_access_body
725            }
726            fn field_mut<'s>(&'s mut self, id: reflectix_core::FieldId) -> Result<reflectix_core::UnsizeableMut<'s>, reflectix_core::FieldAccessError> {
727                #mut_field_access_body
728            }
729
730        }
731
732        impl reflectix_core::TypeInfo for #ty_ident {
733            const INFO: &'static reflectix_core::Type = &#const_def_ident;
734        }
735
736    }
737    .into();
738    tokens
739}