use quote::{format_ident, quote}; use crate::reflection::default_value::gen_get_default_value_fn; use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions}; use crate::reflection::options_attr::OptionsAttr; use crate::util::find_engine_crate_path; pub fn generate(input: syn::ItemStruct, options: OptionsAttr) -> proc_macro2::TokenStream { let engine_crate_path = find_engine_crate_path().unwrap(); if input.generics.params.is_empty() { return gen_impl(&input, None, &engine_crate_path); } let impls = options .impl_with_generics .into_iter() .map(|generic_args| gen_impl(&input, Some(&generic_args), &engine_crate_path)); quote! { #(#impls)* } } fn gen_impl( input: &syn::ItemStruct, generic_args: Option<&syn::AngleBracketedGenericArguments>, engine_crate_path: &syn::Path, ) -> proc_macro2::TokenStream { let fields = input.fields.iter().enumerate().map(|(field_index, field)| { generate_field( &field, field_index, &engine_crate_path, ReflectionFieldGenOptions { field_vis_override: None, gen_get_byte_offset: &|field| { if let Some(field_ident) = &field.ident { quote! { std::mem::offset_of!(Self, #field_ident) } } else { quote! { std::mem::offset_of!(Self, #field_index) } } }, }, ) }); let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args); let input_ident = &input.ident; let generics_type_aliases = input .generics .type_params() .zip( generic_args .iter() .map(|generic_args| &generic_args.args) .flatten() .flat_map(|generic_arg| match generic_arg { syn::GenericArgument::Type(ty) => Some(ty), _ => None, }), ) .map(|(type_param, generic_arg_type)| { let type_param_ident = &type_param.ident; quote! { type #type_param_ident = #generic_arg_type; } }); let struct_pattern = match &input.fields { syn::Fields::Unit => quote! { Self }, syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => { let field_idents = named_fields.iter().map(|field| { let Some(field_ident) = &field.ident else { unreachable!(); }; field_ident }); quote! { Self { #(#field_idents),* } } } syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: unnamed_fields, .. }) => { let field_idents = unnamed_fields .iter() .enumerate() .map(|(field_index, _)| format_ident!("field_{field_index}")); quote! { Self(#(#field_idents),*) } } }; let field_index_match_arms = match &input.fields { syn::Fields::Unit => quote! {}, syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => { let match_arms = named_fields.iter().enumerate().map(|(field_index, field)| { let Some(field_ident) = &field.ident else { unreachable!(); }; quote! { #field_index => { #field_ident } } }); quote! { #(#match_arms)* } } syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: unnamed_fields, .. }) => { let match_arms = unnamed_fields.iter().enumerate().map(|(field_index, _)| { let field_ident = format_ident!("field_{field_index}"); quote! { #field_index => { #field_ident } } }); quote! { #(#match_arms)* } } }; quote! { unsafe impl #engine_crate_path::reflection::Reflection for #input_ident #generic_args { const TYPE_REFLECTION: &#engine_crate_path::reflection::Type = &const { use std::any::Any; use #engine_crate_path::reflection::GetError; #(#generics_type_aliases)* #engine_crate_path::reflection::Type::Struct( #engine_crate_path::reflection::Struct { fields: &[ #(#fields),* ], get_default_value: || { #get_default_value_fn }, cast_dyn_any: |ptr| ptr.cast::(), try_get_field: |target, field_index| { { #![allow(unreachable_code)] let target = target .downcast_ref::() .ok_or(GetError::WrongTargetType)?; let #struct_pattern = target; let field: &dyn Any = match field_index { #field_index_match_arms _ => { return Err(GetError::IndexOutOfBounds); } }; Ok(field) } }, try_get_field_mut: |target, field_index| { { #![allow(unreachable_code)] let target = target .downcast_mut::() .ok_or(GetError::WrongTargetType)?; let #struct_pattern = target; let field: &mut dyn Any = match field_index { #field_index_match_arms _ => { return Err(GetError::IndexOutOfBounds); } }; Ok(field) } }, } ) }; } } }