use crate::{
array::ArrayInfo, enums::EnumInfo, generics::impl_generic_info_methods, list::ListInfo,
map::MapInfo, set::SetInfo, structs::StructInfo, tuple::TupleInfo,
tuple_struct::TupleStructInfo, OpaqueInfo, ReflectKind, Type, TypePathTable,
};
use core::any::{Any, TypeId};
#[derive(Debug, Clone)]
pub enum TypeInfo {
Struct(StructInfo),
TupleStruct(TupleStructInfo),
Tuple(TupleInfo),
List(ListInfo),
Array(ArrayInfo),
Map(MapInfo),
Set(SetInfo),
Enum(EnumInfo),
Opaque(OpaqueInfo),
}
impl TypeInfo {
pub fn ty(&self) -> &Type {
match self {
Self::Struct(info) => info.ty(),
Self::TupleStruct(info) => info.ty(),
Self::Tuple(info) => info.ty(),
Self::List(info) => info.ty(),
Self::Array(info) => info.ty(),
Self::Map(info) => info.ty(),
Self::Set(info) => info.ty(),
Self::Enum(info) => info.ty(),
Self::Opaque(info) => info.ty(),
}
}
#[inline]
pub fn type_id(&self) -> TypeId {
self.ty().id()
}
pub fn type_path_table(&self) -> &TypePathTable {
self.ty().type_path_table()
}
pub fn type_path(&self) -> &'static str {
self.ty().path()
}
pub fn is<T: Any>(&self) -> bool {
self.ty().is::<T>()
}
#[cfg(feature = "reflect_documentation")]
pub fn docs(&self) -> Option<&str> {
match self {
Self::Struct(info) => info.docs(),
Self::TupleStruct(info) => info.docs(),
Self::Tuple(info) => info.docs(),
Self::List(info) => info.docs(),
Self::Array(info) => info.docs(),
Self::Map(info) => info.docs(),
Self::Set(info) => info.docs(),
Self::Enum(info) => info.docs(),
Self::Opaque(info) => info.docs(),
}
}
pub fn kind(&self) -> ReflectKind {
match self {
Self::Struct(_) => ReflectKind::Struct,
Self::TupleStruct(_) => ReflectKind::TupleStruct,
Self::Tuple(_) => ReflectKind::Tuple,
Self::List(_) => ReflectKind::List,
Self::Array(_) => ReflectKind::Array,
Self::Map(_) => ReflectKind::Map,
Self::Set(_) => ReflectKind::Set,
Self::Enum(_) => ReflectKind::Enum,
Self::Opaque(_) => ReflectKind::Opaque,
}
}
impl_generic_info_methods!(self => {
match self {
Self::Struct(info) => info.generics(),
Self::TupleStruct(info) => info.generics(),
Self::Tuple(info) => info.generics(),
Self::List(info) => info.generics(),
Self::Array(info) => info.generics(),
Self::Map(info) => info.generics(),
Self::Set(info) => info.generics(),
Self::Enum(info) => info.generics(),
Self::Opaque(info) => info.generics(),
}
});
}
macro_rules! impl_cast_method {
($name:ident : $kind:ident => $info:ident) => {
#[doc = concat!("Attempts a cast to [`", stringify!($info), "`].")]
#[doc = concat!("\n\nReturns an error if `self` is not [`TypeInfo::", stringify!($kind), "`].")]
pub fn $name(&self) -> Result<&$info, $crate::info::error::TypeInfoError> {
match self {
Self::$kind(info) => Ok(info),
_ => Err($crate::info::error::TypeInfoError::KindMismatch {
expected: ReflectKind::$kind,
received: self.kind(),
}),
}
}
};
}
impl TypeInfo {
impl_cast_method!(as_struct: Struct => StructInfo);
impl_cast_method!(as_tuple_struct: TupleStruct => TupleStructInfo);
impl_cast_method!(as_tuple: Tuple => TupleInfo);
impl_cast_method!(as_list: List => ListInfo);
impl_cast_method!(as_array: Array => ArrayInfo);
impl_cast_method!(as_map: Map => MapInfo);
impl_cast_method!(as_set: Set => SetInfo);
impl_cast_method!(as_enum: Enum => EnumInfo);
impl_cast_method!(as_opaque: Opaque => OpaqueInfo);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::info::typed::Typed;
use crate::TypeInfoError;
use alloc::vec::Vec;
use bevy_platform::collections::HashSet;
#[test]
fn should_return_error_on_invalid_cast() {
let info = <Vec<i32> as Typed>::type_info();
assert!(matches!(
info.as_struct(),
Err(TypeInfoError::KindMismatch {
expected: ReflectKind::Struct,
received: ReflectKind::List
})
));
}
#[test]
fn should_cast_to_set() {
let info = <HashSet<u64> as Typed>::type_info();
assert!(info.as_set().is_ok());
}
}