bevy_reflect: Add Type type (#14838)

# Objective

Closes #7622.

I was working on adding support for reflecting generic functions and
found that I wanted to use an argument's `TypeId` for hashing and
comparison, but its `TypePath` for debugging and error messaging.

While I could just keep them separate, place them in a tuple or a local
struct or something, I think I see an opportunity to make a dedicate
type for this.

Additionally, we can use this type to clean up some duplication amongst
the type info structs in a manner similar to #7622.

## Solution

Added the `Type` type. This should be seen as the most basic
representation of a type apart from `TypeId`. It stores both the
`TypeId` of the type as well as its `TypePathTable`.

The `Hash` and `PartialEq` implementations rely on the `TypeId`, while
the `Debug` implementation relies on the `TypePath`.

This makes it especially useful as a key in a `HashMap` since we get the
speed of the `TypeId` hashing/comparisons with the readability of
`TypePath`.

With this type, we're able to reduce the duplication across the type
info structs by removing individual fields for `TypeId` and
`TypePathTable`, replacing them with a single `Type` field. Similarly,
we can remove many duplicate methods and replace it with a macro that
delegates to the stored `Type`.

### Caveats

It should be noted that this type is currently 3x larger than `TypeId`.
On my machine, it's 48 bytes compared to `TypeId`'s 16. While this
doesn't matter for `TypeInfo` since it would contain that data
regardless, it is something to keep in mind when using elsewhere.

## Testing

All tests should pass as normal:

```
cargo test --package bevy_reflect
```

---

## Showcase

`bevy_reflect` now exports a `Type` struct. This type contains both the
`TypeId` and the `TypePathTable` of the given type, allowing it to be
used like `TypeId` but have the debuggability of `TypePath`.

```rust
// We can create this for any type implementing `TypePath`:
let ty = Type::of::<String>();

// It has `Hash` and `Eq` impls powered by `TypeId`, making it useful for maps:
let mut map = HashMap::<Type, i32>::new();
map.insert(ty, 25);

// And it has a human-readable `Debug` representation:
let debug = format!("{:?}", map);
assert_eq!(debug, "{alloc::string::String: 25}");
```

## Migration Guide

Certain type info structs now only return their item types as `Type`
instead of exposing direct methods on them.

The following methods have been removed:

- `ArrayInfo::item_type_path_table`
- `ArrayInfo::item_type_id`
- `ArrayInfo::item_is`
- `ListInfo::item_type_path_table`
- `ListInfo::item_type_id`
- `ListInfo::item_is`
- `SetInfo::value_type_path_table`
- `SetInfo::value_type_id`
- `SetInfo::value_is`
- `MapInfo::key_type_path_table`
- `MapInfo::key_type_id`
- `MapInfo::key_is`
- `MapInfo::value_type_path_table`
- `MapInfo::value_type_id`
- `MapInfo::value_is`

Instead, access the `Type` directly using one of the new methods:

- `ArrayInfo::item_ty`
- `ListInfo::item_ty`
- `SetInfo::value_ty`
- `MapInfo::key_ty`
- `MapInfo::value_ty`

For example:

```rust
// BEFORE
let type_id = array_info.item_type_id();

// AFTER
let type_id = array_info.item_ty().id();
```
This commit is contained in:
Gino Valente 2024-08-25 10:57:07 -07:00 committed by GitHub
parent f9d7a2ca02
commit 3892adcb47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 343 additions and 513 deletions

View file

@ -1,10 +1,11 @@
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, utility::reflect_hasher, ApplyError, MaybeTyped, PartialReflect, Reflect,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
};
use bevy_reflect_derive::impl_type_path;
use std::{
any::{Any, TypeId},
any::Any,
fmt::{Debug, Formatter},
hash::{Hash, Hasher},
};
@ -77,11 +78,9 @@ pub trait Array: PartialReflect {
/// A container for compile-time array info.
#[derive(Clone, Debug)]
pub struct ArrayInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
item_info: fn() -> Option<&'static TypeInfo>,
item_type_path: TypePathTable,
item_type_id: TypeId,
item_ty: Type,
capacity: usize,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
@ -98,11 +97,9 @@ impl ArrayInfo {
capacity: usize,
) -> Self {
Self {
type_path: TypePathTable::of::<TArray>(),
type_id: TypeId::of::<TArray>(),
ty: Type::of::<TArray>(),
item_info: TItem::maybe_type_info,
item_type_path: TypePathTable::of::<TItem>(),
item_type_id: TypeId::of::<TItem>(),
item_ty: Type::of::<TItem>(),
capacity,
#[cfg(feature = "documentation")]
docs: None,
@ -120,32 +117,7 @@ impl ArrayInfo {
self.capacity
}
/// A representation of the type path of the array.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the array.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the array.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the array type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The [`TypeInfo`] of the array item.
///
@ -155,21 +127,11 @@ impl ArrayInfo {
(self.item_info)()
}
/// A representation of the type path of the array item.
/// The [type] of the array item.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn item_type_path_table(&self) -> &TypePathTable {
&self.item_type_path
}
/// The [`TypeId`] of the array item.
pub fn item_type_id(&self) -> TypeId {
self.item_type_id
}
/// Check if the given type matches the array item type.
pub fn item_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.item_type_id
/// [type]: Type
pub fn item_ty(&self) -> Type {
self.item_ty
}
/// The docstring of this array, if any.

View file

@ -1,7 +1,7 @@
use crate::attributes::{impl_custom_attribute_methods, CustomAttributes};
use crate::{DynamicEnum, PartialReflect, TypePath, TypePathTable, VariantInfo, VariantType};
use crate::type_info::impl_type_methods;
use crate::{DynamicEnum, PartialReflect, Type, TypePath, VariantInfo, VariantType};
use bevy_utils::HashMap;
use std::any::{Any, TypeId};
use std::slice::Iter;
use std::sync::Arc;
@ -135,8 +135,7 @@ pub trait Enum: PartialReflect {
/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).
#[derive(Clone, Debug)]
pub struct EnumInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
variants: Box<[VariantInfo]>,
variant_names: Box<[&'static str]>,
variant_indices: HashMap<&'static str, usize>,
@ -162,8 +161,7 @@ impl EnumInfo {
let variant_names = variants.iter().map(VariantInfo::name).collect();
Self {
type_path: TypePathTable::of::<TEnum>(),
type_id: TypeId::of::<TEnum>(),
ty: Type::of::<TEnum>(),
variants: variants.to_vec().into_boxed_slice(),
variant_names,
variant_indices,
@ -231,32 +229,7 @@ impl EnumInfo {
self.variants.len()
}
/// A representation of the type path of the value.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the value.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the enum.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the enum type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this enum, if any.
#[cfg(feature = "documentation")]

View file

@ -1,6 +1,6 @@
use crate::attributes::{impl_custom_attribute_methods, CustomAttributes};
use crate::{MaybeTyped, PartialReflect, TypeInfo, TypePath, TypePathTable};
use std::any::{Any, TypeId};
use crate::type_info::impl_type_methods;
use crate::{MaybeTyped, PartialReflect, Type, TypeInfo, TypePath};
use std::sync::Arc;
/// The named field of a reflected struct.
@ -8,8 +8,7 @@ use std::sync::Arc;
pub struct NamedField {
name: &'static str,
type_info: fn() -> Option<&'static TypeInfo>,
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
@ -21,8 +20,7 @@ impl NamedField {
Self {
name,
type_info: T::maybe_type_info,
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
docs: None,
@ -57,32 +55,7 @@ impl NamedField {
(self.type_info)()
}
/// A representation of the type path of the field.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the field.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the field.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the field type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this field, if any.
#[cfg(feature = "documentation")]
@ -98,8 +71,7 @@ impl NamedField {
pub struct UnnamedField {
index: usize,
type_info: fn() -> Option<&'static TypeInfo>,
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
@ -110,8 +82,7 @@ impl UnnamedField {
Self {
index,
type_info: T::maybe_type_info,
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
docs: None,
@ -146,32 +117,7 @@ impl UnnamedField {
(self.type_info)()
}
/// A representation of the type path of the field.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the field.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the field.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the field type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this field, if any.
#[cfg(feature = "documentation")]

View file

@ -1,7 +1,8 @@
use alloc::borrow::Cow;
use crate::func::args::{GetOwnership, Ownership};
use crate::TypePath;
use crate::type_info::impl_type_methods;
use crate::{Type, TypePath};
/// Type information for an [`Arg`] used in a [`DynamicFunction`] or [`DynamicFunctionMut`].
///
@ -16,10 +17,10 @@ pub struct ArgInfo {
name: Option<Cow<'static, str>>,
/// The ownership of the argument.
ownership: Ownership,
/// The [type path] of the argument.
/// The [type] of the argument.
///
/// [type path]: TypePath::type_path
type_path: &'static str,
/// [type]: Type
ty: Type,
}
impl ArgInfo {
@ -31,7 +32,7 @@ impl ArgInfo {
index,
name: None,
ownership: T::ownership(),
type_path: T::type_path(),
ty: Type::of::<T>(),
}
}
@ -72,12 +73,7 @@ impl ArgInfo {
self.ownership
}
/// The [type path] of the argument.
///
/// [type path]: TypePath::type_path
pub fn type_path(&self) -> &'static str {
self.type_path
}
impl_type_methods!(ty);
/// Get an ID representing the argument.
///

View file

@ -3,7 +3,8 @@ use alloc::borrow::Cow;
use bevy_utils::all_tuples;
use crate::func::args::{ArgInfo, GetOwnership, Ownership};
use crate::TypePath;
use crate::type_info::impl_type_methods;
use crate::{Type, TypePath};
/// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`].
///
@ -140,7 +141,7 @@ impl FunctionInfo {
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
#[derive(Debug, Clone)]
pub struct ReturnInfo {
type_path: &'static str,
ty: Type,
ownership: Ownership,
}
@ -148,17 +149,14 @@ impl ReturnInfo {
/// Create a new [`ReturnInfo`] representing the given type, `T`.
pub fn new<T: TypePath + GetOwnership>() -> Self {
Self {
type_path: T::type_path(),
ty: Type::of::<T>(),
ownership: T::ownership(),
}
}
/// The type path of the return type.
pub fn type_path(&self) -> &'static str {
self.type_path
}
impl_type_methods!(ty);
/// The ownership of the return type.
/// The ownership of this type.
pub fn ownership(&self) -> Ownership {
self.ownership
}

View file

@ -1719,10 +1719,10 @@ mod tests {
let info = MyList::type_info().as_list().unwrap();
assert!(info.is::<MyList>());
assert!(info.item_is::<usize>());
assert!(info.item_ty().is::<usize>());
assert!(info.item_info().unwrap().is::<usize>());
assert_eq!(MyList::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.item_type_path_table().path());
assert_eq!(usize::type_path(), info.item_ty().path());
let value: &dyn Reflect = &vec![123_usize];
let info = value.get_represented_type_info().unwrap();
@ -1735,10 +1735,10 @@ mod tests {
let info = MySmallVec::type_info().as_list().unwrap();
assert!(info.is::<MySmallVec>());
assert!(info.item_is::<String>());
assert!(info.item_ty().is::<String>());
assert!(info.item_info().unwrap().is::<String>());
assert_eq!(MySmallVec::type_path(), info.type_path());
assert_eq!(String::type_path(), info.item_type_path_table().path());
assert_eq!(String::type_path(), info.item_ty().path());
let value: MySmallVec = smallvec::smallvec![String::default(); 2];
let value: &dyn Reflect = &value;
@ -1751,10 +1751,10 @@ mod tests {
let info = MyArray::type_info().as_array().unwrap();
assert!(info.is::<MyArray>());
assert!(info.item_is::<usize>());
assert!(info.item_ty().is::<usize>());
assert!(info.item_info().unwrap().is::<usize>());
assert_eq!(MyArray::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.item_type_path_table().path());
assert_eq!(usize::type_path(), info.item_ty().path());
assert_eq!(3, info.capacity());
let value: &dyn Reflect = &[1usize, 2usize, 3usize];
@ -1779,13 +1779,10 @@ mod tests {
let info = MyCowSlice::type_info().as_list().unwrap();
assert!(info.is::<MyCowSlice>());
assert!(info.item_is::<u8>());
assert!(info.item_ty().is::<u8>());
assert!(info.item_info().unwrap().is::<u8>());
assert_eq!(std::any::type_name::<MyCowSlice>(), info.type_path());
assert_eq!(
std::any::type_name::<u8>(),
info.item_type_path_table().path()
);
assert_eq!(std::any::type_name::<u8>(), info.item_ty().path());
let value: &dyn Reflect = &Cow::<'static, [u8]>::Owned(vec![0, 1, 2, 3]);
let info = value.get_represented_type_info().unwrap();
@ -1797,13 +1794,13 @@ mod tests {
let info = MyMap::type_info().as_map().unwrap();
assert!(info.is::<MyMap>());
assert!(info.key_is::<usize>());
assert!(info.value_is::<f32>());
assert!(info.key_ty().is::<usize>());
assert!(info.value_ty().is::<f32>());
assert!(info.key_info().unwrap().is::<usize>());
assert!(info.value_info().unwrap().is::<f32>());
assert_eq!(MyMap::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.key_type_path_table().path());
assert_eq!(f32::type_path(), info.value_type_path_table().path());
assert_eq!(usize::type_path(), info.key_ty().path());
assert_eq!(f32::type_path(), info.value_ty().path());
let value: &dyn Reflect = &MyMap::new();
let info = value.get_represented_type_info().unwrap();

View file

@ -1,13 +1,14 @@
use std::any::{Any, TypeId};
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use bevy_reflect_derive::impl_type_path;
use crate::type_info::impl_type_methods;
use crate::utility::reflect_hasher;
use crate::{
self as bevy_reflect, ApplyError, FromReflect, MaybeTyped, PartialReflect, Reflect,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
};
/// A trait used to power [list-like] operations via [reflection].
@ -108,11 +109,9 @@ pub trait List: PartialReflect {
/// A container for compile-time list info.
#[derive(Clone, Debug)]
pub struct ListInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
item_info: fn() -> Option<&'static TypeInfo>,
item_type_path: TypePathTable,
item_type_id: TypeId,
item_ty: Type,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
@ -121,11 +120,9 @@ impl ListInfo {
/// Create a new [`ListInfo`].
pub fn new<TList: List + TypePath, TItem: FromReflect + MaybeTyped + TypePath>() -> Self {
Self {
type_path: TypePathTable::of::<TList>(),
type_id: TypeId::of::<TList>(),
ty: Type::of::<TList>(),
item_info: TItem::maybe_type_info,
item_type_path: TypePathTable::of::<TItem>(),
item_type_id: TypeId::of::<TItem>(),
item_ty: Type::of::<TItem>(),
#[cfg(feature = "documentation")]
docs: None,
}
@ -137,32 +134,7 @@ impl ListInfo {
Self { docs, ..self }
}
/// A representation of the type path of the list.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the list.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the list.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the list type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The [`TypeInfo`] of the list item.
///
@ -172,21 +144,11 @@ impl ListInfo {
(self.item_info)()
}
/// A representation of the type path of the list item.
/// The [type] of the list item.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn item_type_path_table(&self) -> &TypePathTable {
&self.item_type_path
}
/// The [`TypeId`] of the list item.
pub fn item_type_id(&self) -> TypeId {
self.item_type_id
}
/// Check if the given type matches the list item type.
pub fn item_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.item_type_id
/// [type]: Type
pub fn item_ty(&self) -> Type {
self.item_ty
}
/// The docstring of this list, if any.

View file

@ -1,12 +1,12 @@
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::{Entry, HashMap};
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut,
ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
};
/// A trait used to power [map-like] operations via [reflection].
@ -97,14 +97,11 @@ pub trait Map: PartialReflect {
/// A container for compile-time map info.
#[derive(Clone, Debug)]
pub struct MapInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
key_info: fn() -> Option<&'static TypeInfo>,
key_type_path: TypePathTable,
key_type_id: TypeId,
key_ty: Type,
value_info: fn() -> Option<&'static TypeInfo>,
value_type_path: TypePathTable,
value_type_id: TypeId,
value_ty: Type,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
@ -117,14 +114,11 @@ impl MapInfo {
TValue: Reflect + MaybeTyped + TypePath,
>() -> Self {
Self {
type_path: TypePathTable::of::<TMap>(),
type_id: TypeId::of::<TMap>(),
ty: Type::of::<TMap>(),
key_info: TKey::maybe_type_info,
key_type_path: TypePathTable::of::<TKey>(),
key_type_id: TypeId::of::<TKey>(),
key_ty: Type::of::<TKey>(),
value_info: TValue::maybe_type_info,
value_type_path: TypePathTable::of::<TValue>(),
value_type_id: TypeId::of::<TValue>(),
value_ty: Type::of::<TValue>(),
#[cfg(feature = "documentation")]
docs: None,
}
@ -136,32 +130,7 @@ impl MapInfo {
Self { docs, ..self }
}
/// A representation of the type path of the map.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the map.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the map.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the map type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The [`TypeInfo`] of the key type.
///
@ -171,21 +140,11 @@ impl MapInfo {
(self.key_info)()
}
/// A representation of the type path of the key type.
/// The [type] of the key type.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn key_type_path_table(&self) -> &TypePathTable {
&self.key_type_path
}
/// The [`TypeId`] of the key.
pub fn key_type_id(&self) -> TypeId {
self.key_type_id
}
/// Check if the given type matches the key type.
pub fn key_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.key_type_id
/// [type]: Type
pub fn key_ty(&self) -> Type {
self.key_ty
}
/// The [`TypeInfo`] of the value type.
@ -196,21 +155,11 @@ impl MapInfo {
(self.value_info)()
}
/// A representation of the type path of the value type.
/// The [type] of the value type.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn value_type_path_table(&self) -> &TypePathTable {
&self.value_type_path
}
/// The [`TypeId`] of the value.
pub fn value_type_id(&self) -> TypeId {
self.value_type_id
}
/// Check if the given type matches the value type.
pub fn value_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.value_type_id
/// [type]: Type
pub fn value_ty(&self) -> Type {
self.value_ty
}
/// The docstring of this map, if any.

View file

@ -730,8 +730,8 @@ impl<'a, 'de> Visitor<'de> for ArrayVisitor<'a> {
{
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or_default());
let registration = get_registration(
self.array_info.item_type_id(),
self.array_info.item_type_path_table().path(),
self.array_info.item_ty().id(),
self.array_info.item_ty().path(),
self.registry,
)?;
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
@ -770,8 +770,8 @@ impl<'a, 'de> Visitor<'de> for ListVisitor<'a> {
{
let mut list = DynamicList::default();
let registration = get_registration(
self.list_info.item_type_id(),
self.list_info.item_type_path_table().path(),
self.list_info.item_ty().id(),
self.list_info.item_ty().path(),
self.registry,
)?;
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
@ -802,13 +802,13 @@ impl<'a, 'de> Visitor<'de> for MapVisitor<'a> {
{
let mut dynamic_map = DynamicMap::default();
let key_registration = get_registration(
self.map_info.key_type_id(),
self.map_info.key_type_path_table().path(),
self.map_info.key_ty().id(),
self.map_info.key_ty().path(),
self.registry,
)?;
let value_registration = get_registration(
self.map_info.value_type_id(),
self.map_info.value_type_path_table().path(),
self.map_info.value_ty().id(),
self.map_info.value_ty().path(),
self.registry,
)?;
while let Some(key) = map.next_key_seed(TypedReflectDeserializer {
@ -844,8 +844,8 @@ impl<'a, 'de> Visitor<'de> for SetVisitor<'a> {
{
let mut dynamic_set = DynamicSet::default();
let value_registration = get_registration(
self.set_info.value_type_id(),
self.set_info.value_type_path_table().path(),
self.set_info.value_ty().id(),
self.set_info.value_ty().path(),
self.registry,
)?;
while let Some(value) = set.next_element_seed(TypedReflectDeserializer {

View file

@ -1,13 +1,13 @@
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::hashbrown::hash_table::OccupiedEntry as HashTableOccupiedEntry;
use bevy_utils::hashbrown::HashTable;
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, hash_error, ApplyError, PartialReflect, Reflect, ReflectKind, ReflectMut,
ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
};
/// A trait used to power [set-like] operations via [reflection].
@ -82,10 +82,8 @@ pub trait Set: PartialReflect {
/// A container for compile-time set info.
#[derive(Clone, Debug)]
pub struct SetInfo {
type_path: TypePathTable,
type_id: TypeId,
value_type_path: TypePathTable,
value_type_id: TypeId,
ty: Type,
value_ty: Type,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
@ -94,10 +92,8 @@ impl SetInfo {
/// Create a new [`SetInfo`].
pub fn new<TSet: Set + TypePath, TValue: Reflect + TypePath>() -> Self {
Self {
type_path: TypePathTable::of::<TSet>(),
type_id: TypeId::of::<TSet>(),
value_type_path: TypePathTable::of::<TValue>(),
value_type_id: TypeId::of::<TValue>(),
ty: Type::of::<TSet>(),
value_ty: Type::of::<TValue>(),
#[cfg(feature = "documentation")]
docs: None,
}
@ -109,48 +105,13 @@ impl SetInfo {
Self { docs, ..self }
}
/// A representation of the type path of the set.
impl_type_methods!(ty);
/// The [type] of the value.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the set.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the set.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the set type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
/// A representation of the type path of the value type.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn value_type_path_table(&self) -> &TypePathTable {
&self.value_type_path
}
/// The [`TypeId`] of the value.
pub fn value_type_id(&self) -> TypeId {
self.value_type_id
}
/// Check if the given type matches the value type.
pub fn value_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.value_type_id
/// [type]: Type
pub fn value_ty(&self) -> Type {
self.value_ty
}
/// The docstring of this set, if any.

View file

@ -1,17 +1,14 @@
use crate::attributes::{impl_custom_attribute_methods, CustomAttributes};
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, ApplyError, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut,
ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable,
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::HashMap;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use std::{
any::{Any, TypeId},
borrow::Cow,
slice::Iter,
};
use std::{borrow::Cow, slice::Iter};
/// A trait used to power [struct-like] operations via [reflection].
///
@ -78,8 +75,7 @@ pub trait Struct: PartialReflect {
/// A container for compile-time named struct info.
#[derive(Clone, Debug)]
pub struct StructInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
fields: Box<[NamedField]>,
field_names: Box<[&'static str]>,
field_indices: HashMap<&'static str, usize>,
@ -105,8 +101,7 @@ impl StructInfo {
let field_names = fields.iter().map(NamedField::name).collect();
Self {
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
fields: fields.to_vec().into_boxed_slice(),
field_names,
field_indices,
@ -162,32 +157,7 @@ impl StructInfo {
self.fields.len()
}
/// A representation of the type path of the struct.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the struct.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the struct.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the struct type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this struct, if any.
#[cfg(feature = "documentation")]

View file

@ -1,13 +1,14 @@
use bevy_reflect_derive::impl_type_path;
use bevy_utils::all_tuples;
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, utility::GenericTypePathCell, ApplyError, FromReflect,
GetTypeRegistration, MaybeTyped, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo,
GetTypeRegistration, MaybeTyped, Reflect, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo,
TypePath, TypeRegistration, TypeRegistry, Typed, UnnamedField,
};
use crate::{PartialReflect, ReflectKind, TypePathTable};
use std::any::{Any, TypeId};
use crate::{PartialReflect, ReflectKind};
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::slice::Iter;
@ -139,8 +140,7 @@ impl GetTupleField for dyn Tuple {
/// A container for compile-time tuple info.
#[derive(Clone, Debug)]
pub struct TupleInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
fields: Box<[UnnamedField]>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
@ -155,8 +155,7 @@ impl TupleInfo {
///
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
Self {
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
fields: fields.to_vec().into_boxed_slice(),
#[cfg(feature = "documentation")]
docs: None,
@ -184,32 +183,7 @@ impl TupleInfo {
self.fields.len()
}
/// A representation of the type path of the tuple.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the tuple.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the tuple.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the tuple type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this tuple, if any.
#[cfg(feature = "documentation")]

View file

@ -1,11 +1,11 @@
use bevy_reflect_derive::impl_type_path;
use crate::attributes::{impl_custom_attribute_methods, CustomAttributes};
use crate::type_info::impl_type_methods;
use crate::{
self as bevy_reflect, ApplyError, DynamicTuple, PartialReflect, Reflect, ReflectKind,
ReflectMut, ReflectOwned, ReflectRef, Tuple, TypeInfo, TypePath, TypePathTable, UnnamedField,
ReflectMut, ReflectOwned, ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField,
};
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::slice::Iter;
use std::sync::Arc;
@ -58,8 +58,7 @@ pub trait TupleStruct: PartialReflect {
/// A container for compile-time tuple struct info.
#[derive(Clone, Debug)]
pub struct TupleStructInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
fields: Box<[UnnamedField]>,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
@ -75,8 +74,7 @@ impl TupleStructInfo {
///
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
Self {
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
fields: fields.to_vec().into_boxed_slice(),
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
@ -113,32 +111,7 @@ impl TupleStructInfo {
self.fields.len()
}
/// A representation of the type path of the struct.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the struct.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the tuple struct.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the tuple struct type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this struct, if any.
#[cfg(feature = "documentation")]

View file

@ -3,8 +3,10 @@ use crate::{
DynamicTupleStruct, EnumInfo, ListInfo, MapInfo, PartialReflect, Reflect, ReflectKind, SetInfo,
StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable,
};
use core::fmt::Formatter;
use std::any::{Any, TypeId};
use std::fmt::Debug;
use std::hash::Hash;
use thiserror::Error;
/// A static accessor to compile-time type information.
@ -178,36 +180,33 @@ pub enum TypeInfo {
}
impl TypeInfo {
/// The underlying Rust [type].
///
/// [type]: Type
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::Value(info) => info.ty(),
}
}
/// The [`TypeId`] of the underlying type.
pub fn type_id(&self) -> TypeId {
match self {
Self::Struct(info) => info.type_id(),
Self::TupleStruct(info) => info.type_id(),
Self::Tuple(info) => info.type_id(),
Self::List(info) => info.type_id(),
Self::Array(info) => info.type_id(),
Self::Map(info) => info.type_id(),
Self::Set(info) => info.type_id(),
Self::Enum(info) => info.type_id(),
Self::Value(info) => info.type_id(),
}
self.ty().id()
}
/// A representation of the type path of the underlying type.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
match self {
Self::Struct(info) => info.type_path_table(),
Self::TupleStruct(info) => info.type_path_table(),
Self::Tuple(info) => info.type_path_table(),
Self::List(info) => info.type_path_table(),
Self::Array(info) => info.type_path_table(),
Self::Map(info) => info.type_path_table(),
Self::Set(info) => info.type_path_table(),
Self::Enum(info) => info.type_path_table(),
Self::Value(info) => info.type_path_table(),
}
self.ty().type_path_table()
}
/// The [stable, full type path] of the underlying type.
@ -217,12 +216,16 @@ impl TypeInfo {
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
self.ty().path()
}
/// Check if the given type matches the underlying type.
/// Check if the given type matches this one.
///
/// This only compares the [`TypeId`] of the types
/// and does not verify they share the same [`TypePath`]
/// (though it implies they do).
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id()
self.ty().is::<T>()
}
/// The docstring of the underlying type, if any.
@ -287,6 +290,199 @@ impl TypeInfo {
impl_cast_method!(as_value: Value => ValueInfo);
}
/// The base representation of a Rust type.
///
/// When possible, it is recommended to use [`&'static TypeInfo`] instead of this
/// as it provides more information as well as being smaller
/// (since a reference only takes the same number of bytes as a `usize`).
///
/// However, where a static reference to [`TypeInfo`] is not possible,
/// such as with trait objects and other types that can't implement [`Typed`],
/// this type can be used instead.
///
/// It only requires that the type implements [`TypePath`].
///
/// And unlike [`TypeInfo`], this type implements [`Copy`], [`Eq`], and [`Hash`],
/// making it useful as a key type.
///
/// It's especially helpful when compared to [`TypeId`] as it can provide the
/// actual [type path] when debugging, while still having the same performance
/// as hashing/comparing [`TypeId`] directly—at the cost of a little more memory.
///
/// # Examples
///
/// ```
/// use bevy_reflect::{Type, TypePath};
///
/// fn assert_char<T: ?Sized + TypePath>(t: &T) -> Result<(), String> {
/// let ty = Type::of::<T>();
/// if Type::of::<char>() == ty {
/// Ok(())
/// } else {
/// Err(format!("expected `char`, got `{}`", ty.path()))
/// }
/// }
///
/// assert_eq!(
/// assert_char(&'a'),
/// Ok(())
/// );
/// assert_eq!(
/// assert_char(&String::from("Hello, world!")),
/// Err(String::from("expected `char`, got `alloc::string::String`"))
/// );
/// ```
///
/// [`&'static TypeInfo`]: TypeInfo
#[derive(Copy, Clone)]
pub struct Type {
type_path_table: TypePathTable,
type_id: TypeId,
}
impl Type {
/// Create a new [`Type`] from a type that implements [`TypePath`].
pub fn of<T: TypePath + ?Sized>() -> Self {
Self {
type_path_table: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
}
}
/// Returns the [`TypeId`] of the type.
pub fn id(&self) -> TypeId {
self.type_id
}
/// See [`TypePath::type_path`].
pub fn path(&self) -> &'static str {
self.type_path_table.path()
}
/// See [`TypePath::short_type_path`].
pub fn short_path(&self) -> &'static str {
self.type_path_table.short_path()
}
/// See [`TypePath::type_ident`].
pub fn ident(&self) -> Option<&'static str> {
self.type_path_table.ident()
}
/// See [`TypePath::crate_name`].
pub fn crate_name(&self) -> Option<&'static str> {
self.type_path_table.crate_name()
}
/// See [`TypePath::module_path`].
pub fn module_path(&self) -> Option<&'static str> {
self.type_path_table.module_path()
}
/// A representation of the type path of this.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path_table
}
/// Check if the given type matches this one.
///
/// This only compares the [`TypeId`] of the types
/// and does not verify they share the same [`TypePath`]
/// (though it implies they do).
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
}
/// This implementation will only output the [type path] of the type.
///
/// If you need to include the [`TypeId`] in the output,
/// you can access it through [`Type::id`].
///
/// [type path]: TypePath
impl Debug for Type {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.type_path_table.path())
}
}
impl Eq for Type {}
/// This implementation purely relies on the [`TypeId`] of the type,
/// and not on the [type path].
///
/// [type path]: TypePath
impl PartialEq for Type {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
}
}
/// This implementation purely relies on the [`TypeId`] of the type,
/// and not on the [type path].
///
/// [type path]: TypePath
impl Hash for Type {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.type_id.hash(state);
}
}
macro_rules! impl_type_methods {
($field:ident) => {
/// The underlying Rust [type].
///
/// [type]: crate::type_info::Type
pub fn ty(&self) -> &$crate::type_info::Type {
&self.$field
}
/// The [`TypeId`] of this type.
///
/// [`TypeId`]: std::any::TypeId
pub fn type_id(&self) -> ::std::any::TypeId {
self.$field.id()
}
/// The [stable, full type path] of this type.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.$field.path()
}
/// A representation of the type path of this type.
///
/// Provides dynamic access to all methods on [`TypePath`].
///
/// [`TypePath`]: crate::type_path::TypePath
pub fn type_path_table(&self) -> &$crate::type_path::TypePathTable {
&self.$field.type_path_table()
}
/// Check if the given type matches this one.
///
/// This only compares the [`TypeId`] of the types
/// and does not verify they share the same [`TypePath`]
/// (though it implies they do).
///
/// [`TypeId`]: std::any::TypeId
/// [`TypePath`]: crate::type_path::TypePath
pub fn is<T: ::std::any::Any>(&self) -> bool {
self.$field.is::<T>()
}
};
}
pub(crate) use impl_type_methods;
/// A container for compile-time info related to general value types, including primitives.
///
/// This typically represents a type which cannot be broken down any further. This is often
@ -297,8 +493,7 @@ impl TypeInfo {
/// it _as_ a struct. It therefore makes more sense to represent it as a [`ValueInfo`].
#[derive(Debug, Clone)]
pub struct ValueInfo {
type_path: TypePathTable,
type_id: TypeId,
ty: Type,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
@ -306,8 +501,7 @@ pub struct ValueInfo {
impl ValueInfo {
pub fn new<T: Reflect + TypePath + ?Sized>() -> Self {
Self {
type_path: TypePathTable::of::<T>(),
type_id: TypeId::of::<T>(),
ty: Type::of::<T>(),
#[cfg(feature = "documentation")]
docs: None,
}
@ -319,32 +513,7 @@ impl ValueInfo {
Self { docs: doc, ..self }
}
/// A representation of the type path of the value.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the value.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the value.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the value type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
impl_type_methods!(ty);
/// The docstring of this dynamic value, if any.
#[cfg(feature = "documentation")]