summaryrefslogtreecommitdiff
path: root/engine-reflection/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-reflection/src/lib.rs')
-rw-r--r--engine-reflection/src/lib.rs647
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;