diff options
Diffstat (limited to 'engine-macros/src/reflection/struct_impl.rs')
| -rw-r--r-- | engine-macros/src/reflection/struct_impl.rs | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/engine-macros/src/reflection/struct_impl.rs b/engine-macros/src/reflection/struct_impl.rs new file mode 100644 index 0000000..0ba426d --- /dev/null +++ b/engine-macros/src/reflection/struct_impl.rs @@ -0,0 +1,186 @@ +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::<Self>(), + try_get_field: |target, field_index| { { + #![allow(unreachable_code)] + + let target = target + .downcast_ref::<Self>() + .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::<Self>() + .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) + } }, + } + ) + }; + } + } +} |
