diff options
Diffstat (limited to 'engine-reflection/src/lib.rs')
| -rw-r--r-- | engine-reflection/src/lib.rs | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/engine-reflection/src/lib.rs b/engine-reflection/src/lib.rs new file mode 100644 index 0000000..dedfee7 --- /dev/null +++ b/engine-reflection/src/lib.rs @@ -0,0 +1,647 @@ +use std::alloc::Layout; +use std::any::{type_name, Any, TypeId}; +use std::borrow::Cow; +use std::ffi::c_void; +use std::fmt::Debug; + +/// Trait implemented by types that support runtime reflection on them. +/// +/// # Safety +/// Implementors of this trait must provide accurate reflection information in the +/// `TYPE_REFLECTION` associated constant and the `type_reflection` and +/// `get_type_reflection` methods. +pub unsafe trait Reflection: 'static +{ + const TYPE_REFLECTION: &Type; + + fn type_reflection() -> &'static Type + where + Self: Sized, + { + Self::TYPE_REFLECTION + } + + fn get_type_reflection(&self) -> &'static Type + { + Self::TYPE_REFLECTION + } +} + +/// Trait implemented by enums that support runtime reflection on them. +/// +/// # Safety +/// Implementors of this trait must provide accurate reflection information in the +/// `get_variant_reflection` method. +pub unsafe trait EnumReflectionExt: Reflection +{ + fn get_variant_reflection(&self) -> &'static EnumVariant; +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum Type +{ + Struct(Struct), + Enum(Enum), + Array(Array), + Slice(Slice), + Literal(Literal), + Reference(Reference), +} + +impl Type +{ + pub const fn as_struct(&self) -> Option<&Struct> + { + match self { + Self::Struct(struct_reflection) => Some(struct_reflection), + _ => None, + } + } + + pub const fn as_enum(&self) -> Option<&Enum> + { + match self { + Self::Enum(enum_reflection) => Some(enum_reflection), + _ => None, + } + } + + #[inline] + pub fn default_value(&self) -> Option<Box<dyn Any>> + { + match self { + Self::Struct(struct_type) => struct_type.default_value(), + Self::Enum(enum_type) => enum_type.default_value(), + Self::Literal(literal_type) => literal_type.default_value(), + Self::Array(_) | Self::Slice(_) | Self::Reference(_) => None, + } + } + + #[inline] + pub fn has_default_value(&self) -> bool + { + match self { + Self::Struct(struct_type) => struct_type.has_default_value(), + Self::Enum(enum_type) => enum_type.has_default_value(), + Self::Literal(literal_type) => literal_type.has_default_value(), + Self::Array(_) | Self::Slice(_) | Self::Reference(_) => false, + } + } + + #[inline] + pub fn cast_dyn_any(&self, ptr: *mut c_void) -> Option<*mut dyn Any> + { + match self { + Self::Struct(struct_type) => Some((struct_type.cast_dyn_any)(ptr)), + Self::Enum(enum_type) => Some((enum_type.cast_dyn_any)(ptr)), + Self::Literal(literal_type) => Some((literal_type.cast_dyn_any)(ptr)), + Self::Array(array_type) => Some((array_type.cast_dyn_any)(ptr)), + Self::Reference(ref_type) => Some((ref_type.cast_dyn_any)(ptr)), + Self::Slice(_) => None, + } + } +} + +#[derive(Debug, Clone)] +pub struct Struct +{ + pub fields: &'static [Field], + pub get_default_value: fn() -> Option<DefaultValueFn>, + pub cast_dyn_any: CastDynAnyFn, + pub try_get_field: + fn(target: &dyn Any, field_index: usize) -> Result<&dyn Any, GetError>, + pub try_get_field_mut: + fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>, +} + +impl Struct +{ + #[inline] + pub fn default_value(&self) -> Option<Box<dyn Any>> + { + Some((self.get_default_value)()?()) + } + + #[inline] + pub fn has_default_value(&self) -> bool + { + (self.get_default_value)().is_some() + } +} + +#[derive(Debug, Clone)] +pub struct Enum +{ + /// Enum variants in the same order as in the enum definition. + pub variants: &'static [EnumVariant], + + /// The enum only contains unit variants. + pub is_unit_only: bool, + + pub get_default_value: fn() -> Option<DefaultValueFn>, + pub cast_dyn_any: CastDynAnyFn, + + pub get_variant_index: fn(&dyn Any) -> Option<usize>, +} + +impl Enum +{ + #[inline] + pub fn default_value(&self) -> Option<Box<dyn Any>> + { + Some((self.get_default_value)()?()) + } + + #[inline] + pub fn has_default_value(&self) -> bool + { + (self.get_default_value)().is_some() + } +} + +#[derive(Debug, Clone)] +pub struct EnumVariant +{ + pub name: &'static str, + + /// The fields of this variant. If `None`, this variant is a unit variant. + pub fields: Option<EnumVariantFields>, + + pub try_write_new_to: fn( + dst: &mut dyn Any, + fields: &mut dyn Iterator<Item = Box<dyn Any>>, + ) -> Result<(), EnumVariantWriteNewToError>, + + pub try_get_field: + fn(target: &dyn Any, field_index: usize) -> Result<&dyn Any, GetError>, + + pub try_get_field_mut: + fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>, +} + +#[derive(Debug, thiserror::Error)] +pub enum EnumVariantWriteNewToError +{ + #[error("Destination must have the same type as the variant's enum")] + WrongDstType, + + #[error("Too few fields are specified")] + TooFewFields, + + #[error("Too many fields are specified")] + TooManyFields, + + #[error("A field has the wrong type")] + WrongFieldType, +} + +#[derive(Debug, thiserror::Error)] +pub enum GetError +{ + #[error("Target is not the correct type")] + WrongTargetType, + + #[error("Target is not the correct enum variant")] + WrongTargetEnumVariant, + + #[error("Index is out of bounds")] + IndexOutOfBounds, +} + +#[derive(Debug, Clone)] +pub enum EnumVariantFields +{ + Named + { + fields: &'static [EnumVariantField] + }, + Unnamed + { + fields: &'static [EnumVariantField] + }, +} + +impl EnumVariantFields +{ + pub fn fields(&self) -> &'static [EnumVariantField] + { + match self { + Self::Named { fields } => fields, + Self::Unnamed { fields } => fields, + } + } +} + +#[derive(Debug, Clone)] +pub struct EnumVariantField +{ + pub name: Option<&'static str>, + pub index: usize, + pub type_id: TypeId, + pub get_type_name: FnWithDebug<&'static str>, + pub get_type: FnWithDebug<Option<&'static Type>>, +} + +impl EnumVariantField +{ + pub fn type_name(&self) -> &'static str + { + self.get_type_name.get() + } + + pub fn type_reflection(&self) -> Option<&'static Type> + { + self.get_type.get() + } +} + +#[derive(Debug, Clone)] +pub struct Field +{ + pub name: Option<&'static str>, + pub index: usize, + pub layout: Layout, + + /// Byte offset to the field relative to the start of the type containing it. + pub byte_offset: usize, + + pub type_id: TypeId, + pub get_type_name: FnWithDebug<&'static str>, + pub get_type: FnWithDebug<Option<&'static Type>>, + pub visibility: Visibility, +} + +impl Field +{ + pub fn type_name(&self) -> &'static str + { + self.get_type_name.get() + } + + pub fn type_reflection(&self) -> Option<&'static Type> + { + self.get_type.get() + } +} + +#[derive(Debug, Clone)] +pub struct Array +{ + pub item_type: &'static Type, + pub item_layout: Layout, + pub get_item_type_name: FnWithDebug<&'static str>, + pub length: usize, + pub cast_dyn_any: CastDynAnyFn, + pub try_get_item: fn(target: &dyn Any, index: usize) -> Result<&dyn Any, GetError>, + pub try_get_item_mut: + fn(target: &mut dyn Any, index: usize) -> Result<&mut dyn Any, GetError>, +} + +impl Array +{ + pub fn item_type_name(&self) -> &'static str + { + self.get_item_type_name.get() + } +} + +#[derive(Debug, Clone)] +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>, + pub try_get_len: fn(target: &dyn Any) -> Option<usize>, +} + +impl Slice +{ + pub fn item_type_name(&self) -> &'static str + { + self.get_item_type_name.get() + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub struct Literal +{ + pub layout: Layout, + pub type_id: TypeId, + pub ty: LiteralType, + pub type_name: fn() -> &'static str, + pub get_default_value: fn() -> Option<DefaultValueFn>, + pub cast_dyn_any: CastDynAnyFn, +} + +impl Literal +{ + #[inline] + pub fn default_value(&self) -> Option<Box<dyn Any>> + { + Some((self.get_default_value)()?()) + } + + #[inline] + pub fn has_default_value(&self) -> bool + { + (self.get_default_value)().is_some() + } +} + +#[derive(Debug)] +pub struct Reference +{ + pub ty: &'static Type, + pub cast_dyn_any: CastDynAnyFn, + pub try_deref: fn(target: &dyn Any) -> Option<&dyn Any>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum LiteralType +{ + U8, + I8, + U16, + I16, + U32, + I32, + U64, + I64, + U128, + I128, + F32, + F64, + Usize, + Isize, + Bool, + Str, +} + +#[derive(Debug, Clone)] +pub enum Visibility +{ + Pub, + PubScoped(VisibilityScope), + Private, +} + +#[derive(Debug, Clone)] +pub enum VisibilityScope +{ + Crate, + Super, + SelfModule, + In(Cow<'static, str>), +} + +macro_rules! impl_reflection_for_literals { + ($(($literal_type: ident, $literal: ty)),*) => { + $( + unsafe impl Reflection for $literal + { + const TYPE_REFLECTION: &Type = &Type::Literal(Literal { + layout: Layout::new::<$literal>(), + type_id: TypeId::of::<$literal>(), + ty: LiteralType::$literal_type, + type_name: || type_name::<$literal>(), + get_default_value: || Some(|| Box::new(Self::default())), + cast_dyn_any: |ptr| ptr.cast::<Self>() + }); + } + )* + }; +} + +impl_reflection_for_literals!( + (U8, u8), + (I8, i8), + (U16, u16), + (I16, i16), + (U32, u32), + (I32, i32), + (U64, u64), + (I64, i64), + (U128, u128), + (I128, i128), + (F32, f32), + (F64, f64), + (Usize, usize), + (Isize, isize), + (Bool, bool), + (Str, &'static str) +); + +unsafe impl<T: Reflection, const LEN: usize> Reflection for [T; LEN] +{ + const TYPE_REFLECTION: &Type = &Type::Array(Array { + item_type: T::TYPE_REFLECTION, + item_layout: Layout::new::<T>(), + get_item_type_name: FnWithDebug::new(|| type_name::<T>()), + length: LEN, + cast_dyn_any: |ptr| ptr.cast::<Self>(), + try_get_item: |target, index| { + let target = target + .downcast_ref::<Self>() + .ok_or(GetError::WrongTargetType)?; + + let item = target.get(index).ok_or(GetError::IndexOutOfBounds)?; + + Ok(item) + }, + try_get_item_mut: |target, index| { + let target = target + .downcast_mut::<Self>() + .ok_or(GetError::WrongTargetType)?; + + let item = target.get_mut(index).ok_or(GetError::IndexOutOfBounds)?; + + Ok(item) + }, + }); +} + +unsafe impl<T: Reflection> Reflection for &'static [T] +{ + const TYPE_REFLECTION: &Type = &Type::Slice(Slice { + item_type: T::TYPE_REFLECTION, + item_layout: Layout::new::<T>(), + get_item_type_name: FnWithDebug::new(|| type_name::<T>()), + try_get_item: |target, index| { + let target = target + .downcast_ref::<Self>() + .ok_or(GetError::WrongTargetType)?; + + let item = target.get(index).ok_or(GetError::IndexOutOfBounds)?; + + Ok(item) + }, + try_get_len: |target| { + let target = target.downcast_ref::<Self>()?; + + Some(target.len()) + }, + }); +} + +unsafe impl<T: Reflection> Reflection for &'static T +{ + const TYPE_REFLECTION: &Type = &Type::Reference(Reference { + ty: T::TYPE_REFLECTION, + cast_dyn_any: |ptr| ptr.cast::<Self>(), + try_deref: |target| { + let target = target.downcast_ref::<Self>()?; + + Some(*target) + }, + }); +} + +unsafe impl<T: Reflection> Reflection for Option<T> +{ + const TYPE_REFLECTION: &Type = &Type::Enum(Enum { + variants: &[ + EnumVariant { + name: "Some", + fields: Some(EnumVariantFields::Unnamed { + fields: &[EnumVariantField { + name: None, + index: 0, + type_id: TypeId::of::<T>(), + get_type_name: FnWithDebug::new(|| type_name::<T>()), + get_type: FnWithDebug::new(|| Some(T::type_reflection())), + }], + }), + try_write_new_to: |dst, fields| { + let dst = dst + .downcast_mut::<Self>() + .ok_or(EnumVariantWriteNewToError::WrongDstType)?; + + let value_field = fields + .next() + .ok_or(EnumVariantWriteNewToError::TooFewFields)? + .downcast::<T>() + .map_err(|_| EnumVariantWriteNewToError::WrongFieldType)?; + + if fields.next().is_some() { + return Err(EnumVariantWriteNewToError::TooManyFields); + } + + *dst = Some(*value_field); + + Ok(()) + }, + try_get_field: |target, field_index| { + if field_index != 0 { + return Err(GetError::IndexOutOfBounds); + } + + let target = target + .downcast_ref::<Self>() + .ok_or(GetError::WrongTargetType)?; + + let Some(value_field) = target else { + return Err(GetError::WrongTargetEnumVariant); + }; + + Ok(value_field) + }, + try_get_field_mut: |target, field_index| { + if field_index != 0 { + return Err(GetError::IndexOutOfBounds); + } + + let target = target + .downcast_mut::<Self>() + .ok_or(GetError::WrongTargetType)?; + + let Some(value_field) = target else { + return Err(GetError::WrongTargetEnumVariant); + }; + + Ok(value_field) + }, + }, + EnumVariant { + name: "None", + fields: None, + try_write_new_to: |dst, fields| { + let dst = dst + .downcast_mut::<Self>() + .ok_or(EnumVariantWriteNewToError::WrongDstType)?; + + if fields.next().is_some() { + return Err(EnumVariantWriteNewToError::TooManyFields); + } + + *dst = None; + + Ok(()) + }, + try_get_field: |_, _| Err(GetError::IndexOutOfBounds), + try_get_field_mut: |_, _| Err(GetError::IndexOutOfBounds), + }, + ], + is_unit_only: false, + get_default_value: || Some(|| Box::new(Option::<T>::None)), + cast_dyn_any: |ptr| ptr.cast::<Self>(), + get_variant_index: |target| { + let target = target.downcast_ref::<Self>()?; + + Some(match target { + Some(_) => 0, + None => 1, + }) + }, + }); +} + +unsafe impl<T: Reflection> EnumReflectionExt for Option<T> +{ + fn get_variant_reflection(&self) -> &'static EnumVariant + { + let ty = unsafe { Self::type_reflection().as_enum().unwrap_unchecked() }; + + match self { + Some(_) => &ty.variants[0], + None => &ty.variants[1], + } + } +} + +#[derive(Clone)] +pub struct FnWithDebug<Value> +{ + func: fn() -> Value, +} + +impl<Value> FnWithDebug<Value> +{ + pub const fn new(func: fn() -> Value) -> Self + { + Self { func } + } + + pub fn get(&self) -> Value + { + (self.func)() + } +} + +impl<Value: Debug> Debug for FnWithDebug<Value> +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + formatter + .debug_tuple("FnWithDebug") + .field(&self.get()) + .finish() + } +} + +type DefaultValueFn = fn() -> Box<dyn Any>; + +type CastDynAnyFn = fn(*mut c_void) -> *mut dyn Any; |
