From eaff58026be19c068005d6e90bd9e8cbe985b093 Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 8 Jun 2026 17:19:46 +0200 Subject: feat(engine): add enum variant field reflection --- engine-macros/src/lib.rs | 231 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 165 insertions(+), 66 deletions(-) (limited to 'engine-macros/src/lib.rs') diff --git a/engine-macros/src/lib.rs b/engine-macros/src/lib.rs index e7b3772..3eecab3 100644 --- a/engine-macros/src/lib.rs +++ b/engine-macros/src/lib.rs @@ -3,9 +3,10 @@ use std::fmt::Write; use proc_macro::TokenStream; -use quote::{format_ident, quote, ToTokens}; +use quote::{quote, ToTokens}; use syn::{ parse, + Field as SynField, Fields, Item, ItemEnum, @@ -59,68 +60,21 @@ fn generate_struct_reflection_impl(input: ItemStruct) -> TokenStream let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); - let fields = input.fields.into_iter().enumerate().map(|(index, field)| { - let field_ident = field.ident.unwrap_or_else(|| format_ident!("{index}")); - - let field_type = &field.ty; - - let field_name = LitStr::new(&field_ident.to_string(), field_ident.span()); - - // since std::any::type_name as const is not stable yet - let field_type_name = field_type.to_token_stream().to_string(); - - let field_vis = gen_reflection_visibility_path(&field.vis, &engine_crate_path); - - quote! { - #engine_crate_path::reflection::StructField { - name: #field_name, - index: #index, - layout: std::alloc::Layout::new::<#field_type>(), - byte_offset: std::mem::offset_of!(Self, #field_ident), - type_id: std::any::TypeId::of::<#field_type>(), - type_name: #field_type_name, - get_type: #engine_crate_path::reflection::FnWithDebug::new(|| { - struct SpecializationTarget(std::marker::PhantomData); - - trait FieldHasReflection - { - fn field_type_reflection(&self) - -> Option<&'static #engine_crate_path::reflection::Type>; - } - - trait FieldDoesNotHaveReflection - { - fn field_type_reflection(&self) - -> Option<&'static #engine_crate_path::reflection::Type>; - } - - impl FieldDoesNotHaveReflection for &SpecializationTarget - { - fn field_type_reflection(&self) - -> Option<&'static #engine_crate_path::reflection::Type> - { - None - } - } - - impl FieldHasReflection for SpecializationTarget - where - Field: #engine_crate_path::reflection::Reflection - { - fn field_type_reflection(&self) - -> Option<&'static #engine_crate_path::reflection::Type> - { - Some(Field::type_reflection()) - } - } - - (&SpecializationTarget::<#field_type>(std::marker::PhantomData)) - .field_type_reflection() - }), - visibility: #field_vis - } - } - }); + let fields = input + .fields + .into_iter() + .enumerate() + .map(|(field_index, field)| { + gen_reflection_field( + &field, + field_index, + &engine_crate_path, + ReflectionFieldGenOptions { + field_vis_override: None, + include_byte_offset: true, + }, + ) + }); quote! { unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for @@ -152,9 +106,56 @@ fn generate_enum_reflection_impl(input: ItemEnum) -> TokenStream let variants = input.variants.iter().map(|variant| { let variant_name = LitStr::new(&variant.ident.to_string(), variant.ident.span()); + let fields = match &variant.fields { + Fields::Unit => quote! { None }, + Fields::Named(named_fields) => { + let fields = named_fields.named.iter().enumerate().map( + |(variant_field_index, variant_field)| { + gen_reflection_field( + variant_field, + variant_field_index, + &engine_crate_path, + ReflectionFieldGenOptions { + field_vis_override: None, + include_byte_offset: false, + }, + ) + }, + ); + + quote! { + Some(#engine_crate_path::reflection::EnumVariantFields::Named { + fields: &[#(#fields),*] + }) + } + } + Fields::Unnamed(unnamed_fields) => { + let fields = unnamed_fields.unnamed.iter().enumerate().map( + |(variant_field_index, variant_field)| { + gen_reflection_field( + variant_field, + variant_field_index, + &engine_crate_path, + ReflectionFieldGenOptions { + field_vis_override: None, + include_byte_offset: false, + }, + ) + }, + ); + + quote! { + Some(#engine_crate_path::reflection::EnumVariantFields::Unnamed { + fields: &[#(#fields),*] + }) + } + } + }; + quote! { #engine_crate_path::reflection::EnumVariant { name: #variant_name, + fields: #fields } } }); @@ -174,6 +175,11 @@ fn generate_enum_reflection_impl(input: ItemEnum) -> TokenStream } }); + let is_unit_only = input + .variants + .iter() + .all(|variant| matches!(variant.fields, Fields::Unit)); + quote! { unsafe impl #impl_generics #engine_crate_path::reflection::Reflection for #input_ident #type_generics #where_clause @@ -182,9 +188,8 @@ fn generate_enum_reflection_impl(input: ItemEnum) -> TokenStream &const { #engine_crate_path::reflection::Type::Enum( #engine_crate_path::reflection::Enum { - variants: &[ - #(#variants),* - ] + variants: &[#(#variants),*], + is_unit_only: #is_unit_only } ) }; @@ -211,6 +216,100 @@ fn generate_enum_reflection_impl(input: ItemEnum) -> TokenStream .into() } +struct ReflectionFieldGenOptions +{ + field_vis_override: Option, + include_byte_offset: bool, +} + +fn gen_reflection_field( + field: &SynField, + field_index: usize, + engine_crate_path: &SynPath, + options: ReflectionFieldGenOptions, +) -> proc_macro2::TokenStream +{ + let field_ident = &field.ident; + + let field_type = &field.ty; + + let field_name = if let Some(field_ident) = field_ident { + let field_name = LitStr::new(&field_ident.to_string(), field_ident.span()); + + quote! { Some(#field_name) } + } else { + quote! { None } + }; + + let field_byte_offset = if options.include_byte_offset { + if let Some(field_ident) = field_ident { + quote! { Some(std::mem::offset_of!(Self, #field_ident)) } + } else { + quote! { Some(std::mem::offset_of!(Self, #field_index)) } + } + } else { + quote! { None } + }; + + // since std::any::type_name as const is not stable yet + let field_type_name = field_type.to_token_stream().to_string(); + + let field_vis = options.field_vis_override.as_ref().unwrap_or(&field.vis); + + let field_reflection_vis = + gen_reflection_visibility_path(field_vis, &engine_crate_path); + + quote! { + #engine_crate_path::reflection::Field { + name: #field_name, + index: #field_index, + layout: std::alloc::Layout::new::<#field_type>(), + byte_offset: #field_byte_offset, + type_id: std::any::TypeId::of::<#field_type>(), + type_name: #field_type_name, + get_type: #engine_crate_path::reflection::FnWithDebug::new(|| { + struct SpecializationTarget(std::marker::PhantomData); + + trait FieldHasReflection + { + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type>; + } + + trait FieldDoesNotHaveReflection + { + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type>; + } + + impl FieldDoesNotHaveReflection for &SpecializationTarget + { + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type> + { + None + } + } + + impl FieldHasReflection for SpecializationTarget + where + Field: #engine_crate_path::reflection::Reflection + { + fn field_type_reflection(&self) + -> Option<&'static #engine_crate_path::reflection::Type> + { + Some(Field::type_reflection()) + } + } + + (&SpecializationTarget::<#field_type>(std::marker::PhantomData)) + .field_type_reflection() + }), + visibility: #field_reflection_vis + } + } +} + fn gen_reflection_visibility_path( visibility: &SynVisibility, engine_crate_path: &SynPath, -- cgit v1.2.3-18-g5258