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 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 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 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 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 .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 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 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 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 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}