From 8dc188fbe6224155f6a48f8908402e98b169d0a6 Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 16 Jul 2026 20:17:51 +0200 Subject: feat(engine): add try_get_* fns to struct, array & slice reflection --- engine-macros/src/reflection/enum_impl.rs | 8 ++-- engine-macros/src/reflection/struct_impl.rs | 72 ++++++++++++++++++++++++++++- engine-reflection/src/lib.rs | 51 +++++++++++++------- 3 files changed, 109 insertions(+), 22 deletions(-) diff --git a/engine-macros/src/reflection/enum_impl.rs b/engine-macros/src/reflection/enum_impl.rs index b637d9f..37abc34 100644 --- a/engine-macros/src/reflection/enum_impl.rs +++ b/engine-macros/src/reflection/enum_impl.rs @@ -356,20 +356,20 @@ fn generate_variants<'a>( #![allow(unreachable_code)] use std::any::Any; - use #engine_crate_path::reflection::EnumVariantGetFieldError; + use #engine_crate_path::reflection::GetError; let target = target .downcast_mut::() - .ok_or(EnumVariantGetFieldError::WrongTargetType)?; + .ok_or(GetError::WrongTargetType)?; let #variant_pattern = target else { - return Err(EnumVariantGetFieldError::WrongTargetVariant); + return Err(GetError::WrongTargetEnumVariant); }; let field: &mut dyn Any = match field_index { #field_index_match_arms _ => { - return Err(EnumVariantGetFieldError::FieldIndexOutOfBounds); + return Err(GetError::IndexOutOfBounds); } }; diff --git a/engine-macros/src/reflection/struct_impl.rs b/engine-macros/src/reflection/struct_impl.rs index 4fbf88b..8617874 100644 --- a/engine-macros/src/reflection/struct_impl.rs +++ b/engine-macros/src/reflection/struct_impl.rs @@ -1,4 +1,4 @@ -use quote::quote; +use quote::{format_ident, quote}; use crate::reflection::default_value::gen_get_default_value_fn; use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions}; @@ -73,12 +73,64 @@ fn gen_impl( } }); + 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( @@ -89,7 +141,23 @@ fn gen_impl( get_default_value: || { #get_default_value_fn }, - cast_dyn_any: |ptr| ptr.cast::() + cast_dyn_any: |ptr| ptr.cast::(), + try_get_field_mut: |target, field_index| { + 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) + }, } ) }; diff --git a/engine-reflection/src/lib.rs b/engine-reflection/src/lib.rs index 79a7417..4ef688c 100644 --- a/engine-reflection/src/lib.rs +++ b/engine-reflection/src/lib.rs @@ -109,6 +109,8 @@ pub struct Struct pub fields: &'static [Field], pub get_default_value: fn() -> Option, pub cast_dyn_any: CastDynAnyFn, + pub try_get_field_mut: + fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>, } impl Struct @@ -169,10 +171,8 @@ pub struct EnumVariant fields: &mut dyn Iterator>, ) -> Result<(), EnumVariantWriteNewToError>, - pub try_get_field_mut: fn( - target: &mut dyn Any, - field_index: usize, - ) -> Result<&mut dyn Any, EnumVariantGetFieldError>, + pub try_get_field_mut: + fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>, } #[derive(Debug, thiserror::Error)] @@ -192,16 +192,16 @@ pub enum EnumVariantWriteNewToError } #[derive(Debug, thiserror::Error)] -pub enum EnumVariantGetFieldError +pub enum GetError { - #[error("Target is not the same type as the variant's enum")] + #[error("Target is not the correct type")] WrongTargetType, - #[error("Target is not the correct variant")] - WrongTargetVariant, + #[error("Target is not the correct enum variant")] + WrongTargetEnumVariant, - #[error("Field index is out of bounds")] - FieldIndexOutOfBounds, + #[error("Index is out of bounds")] + IndexOutOfBounds, } #[derive(Debug, Clone)] @@ -288,6 +288,8 @@ pub struct Array pub get_item_type_name: FnWithDebug<&'static str>, pub length: usize, pub cast_dyn_any: CastDynAnyFn, + pub try_get_item_mut: + fn(target: &mut dyn Any, index: usize) -> Result<&mut dyn Any, GetError>, } impl Array @@ -304,6 +306,7 @@ pub struct Slice pub item_type: &'static Type, pub item_layout: Layout, pub get_item_type_name: FnWithDebug<&'static str>, + pub try_get_item: fn(target: &dyn Any, index: usize) -> Result<&dyn Any, GetError>, } impl Slice @@ -432,6 +435,15 @@ unsafe impl Reflection for [T; LEN] get_item_type_name: FnWithDebug::new(|| type_name::()), length: LEN, cast_dyn_any: |ptr| ptr.cast::(), + try_get_item_mut: |target, index| { + let target = target + .downcast_mut::() + .ok_or(GetError::WrongTargetType)?; + + let item = target.get_mut(index).ok_or(GetError::IndexOutOfBounds)?; + + Ok(item) + }, }); } @@ -441,6 +453,15 @@ unsafe impl Reflection for &'static [T] item_type: T::TYPE_REFLECTION, item_layout: Layout::new::(), get_item_type_name: FnWithDebug::new(|| type_name::()), + try_get_item: |target, index| { + let target = target + .downcast_ref::() + .ok_or(GetError::WrongTargetType)?; + + let item = target.get(index).ok_or(GetError::IndexOutOfBounds)?; + + Ok(item) + }, }); } @@ -488,15 +509,15 @@ unsafe impl Reflection for Option }, try_get_field_mut: |target, field_index| { if field_index != 0 { - return Err(EnumVariantGetFieldError::FieldIndexOutOfBounds); + return Err(GetError::IndexOutOfBounds); } let target = target .downcast_mut::() - .ok_or(EnumVariantGetFieldError::WrongTargetType)?; + .ok_or(GetError::WrongTargetType)?; let Some(value_field) = target else { - return Err(EnumVariantGetFieldError::WrongTargetVariant); + return Err(GetError::WrongTargetEnumVariant); }; Ok(value_field) @@ -518,9 +539,7 @@ unsafe impl Reflection for Option Ok(()) }, - try_get_field_mut: |_, _| { - Err(EnumVariantGetFieldError::FieldIndexOutOfBounds) - }, + try_get_field_mut: |_, _| Err(GetError::IndexOutOfBounds), }, ], is_unit_only: false, -- cgit v1.2.3-18-g5258