bevy_reflect: Add dynamic type data access and iteration to TypeRegistration
(#15347)
# Objective There's currently no way to iterate through all the type data in a `TypeRegistration`. While these are all type-erased, it can still be useful to see what types (by `TypeId`) are registered for a given type. Additionally, it might be good to have ways of dynamically working with `TypeRegistration`. ## Solution Added a way to iterate through all type data on a given `TypeRegistration`. This PR also adds methods for working with type data dynamically as well as methods for conveniently checking if a given type data exists on the registration. I also took this opportunity to reorganize the methods on `TypeRegistration` as it has always bothered me haha (i.e. the constructor not being at the top, etc.). ## Testing You can test locally by running: ``` cargo test --package bevy_reflect ``` --- ## Showcase The type-erased type data on a `TypeRegistration` can now be iterated! ```rust #[derive(Reflect)] struct Foo; #[derive(Clone)] struct DataA(i32); #[derive(Clone)] struct DataB(i32); let mut registration = TypeRegistration::of::<Foo>(); registration.insert(DataA(123)); registration.insert(DataB(456)); let mut iter = registration.iter(); let (id, data) = iter.next().unwrap(); assert_eq!(id, TypeId::of::<DataA>()); assert_eq!(data.downcast_ref::<DataA>().unwrap().0, 123); let (id, data) = iter.next().unwrap(); assert_eq!(id, TypeId::of::<DataB>()); assert_eq!(data.downcast_ref::<DataB>().unwrap().0, 456); assert!(iter.next().is_none()); ```
This commit is contained in:
parent
55dddaf72e
commit
51accd34ed
@ -3,6 +3,7 @@ use bevy_ptr::{Ptr, PtrMut};
|
|||||||
use bevy_utils::{HashMap, HashSet, TypeIdMap};
|
use bevy_utils::{HashMap, HashSet, TypeIdMap};
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
@ -478,51 +479,142 @@ impl Debug for TypeRegistration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TypeRegistration {
|
impl TypeRegistration {
|
||||||
|
/// Creates type registration information for `T`.
|
||||||
|
pub fn of<T: Reflect + Typed + TypePath>() -> Self {
|
||||||
|
Self {
|
||||||
|
data: Default::default(),
|
||||||
|
type_info: T::type_info(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`TypeId`] of the type.
|
/// Returns the [`TypeId`] of the type.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn type_id(&self) -> TypeId {
|
pub fn type_id(&self) -> TypeId {
|
||||||
self.type_info.type_id()
|
self.type_info.type_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the value of type `T` in this registration's type
|
|
||||||
/// data.
|
|
||||||
///
|
|
||||||
/// Returns `None` if no such value exists.
|
|
||||||
pub fn data<T: TypeData>(&self) -> Option<&T> {
|
|
||||||
self.data
|
|
||||||
.get(&TypeId::of::<T>())
|
|
||||||
.and_then(|value| value.downcast_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the value of type `T` in this
|
|
||||||
/// registration's type data.
|
|
||||||
///
|
|
||||||
/// Returns `None` if no such value exists.
|
|
||||||
pub fn data_mut<T: TypeData>(&mut self) -> Option<&mut T> {
|
|
||||||
self.data
|
|
||||||
.get_mut(&TypeId::of::<T>())
|
|
||||||
.and_then(|value| value.downcast_mut())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the registration's [`TypeInfo`]
|
/// Returns a reference to the registration's [`TypeInfo`]
|
||||||
pub fn type_info(&self) -> &'static TypeInfo {
|
pub fn type_info(&self) -> &'static TypeInfo {
|
||||||
self.type_info
|
self.type_info
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts an instance of `T` into this registration's type data.
|
/// Inserts an instance of `T` into this registration's [type data].
|
||||||
///
|
///
|
||||||
/// If another instance of `T` was previously inserted, it is replaced.
|
/// If another instance of `T` was previously inserted, it is replaced.
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
pub fn insert<T: TypeData>(&mut self, data: T) {
|
pub fn insert<T: TypeData>(&mut self, data: T) {
|
||||||
self.data.insert(TypeId::of::<T>(), Box::new(data));
|
self.data.insert(TypeId::of::<T>(), Box::new(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates type registration information for `T`.
|
/// Returns a reference to the value of type `T` in this registration's
|
||||||
pub fn of<T: Reflect + Typed + TypePath>() -> Self {
|
/// [type data].
|
||||||
Self {
|
///
|
||||||
data: Default::default(),
|
/// Returns `None` if no such value exists.
|
||||||
type_info: T::type_info(),
|
///
|
||||||
}
|
/// For a dynamic version of this method, see [`data_by_id`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`data_by_id`]: Self::data_by_id
|
||||||
|
pub fn data<T: TypeData>(&self) -> Option<&T> {
|
||||||
|
self.data
|
||||||
|
.get(&TypeId::of::<T>())
|
||||||
|
.and_then(|value| value.downcast_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the value with the given [`TypeId`] in this registration's
|
||||||
|
/// [type data].
|
||||||
|
///
|
||||||
|
/// Returns `None` if no such value exists.
|
||||||
|
///
|
||||||
|
/// For a static version of this method, see [`data`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`data`]: Self::data
|
||||||
|
pub fn data_by_id(&self, type_id: TypeId) -> Option<&dyn TypeData> {
|
||||||
|
self.data.get(&type_id).map(Deref::deref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the value of type `T` in this registration's
|
||||||
|
/// [type data].
|
||||||
|
///
|
||||||
|
/// Returns `None` if no such value exists.
|
||||||
|
///
|
||||||
|
/// For a dynamic version of this method, see [`data_mut_by_id`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`data_mut_by_id`]: Self::data_mut_by_id
|
||||||
|
pub fn data_mut<T: TypeData>(&mut self) -> Option<&mut T> {
|
||||||
|
self.data
|
||||||
|
.get_mut(&TypeId::of::<T>())
|
||||||
|
.and_then(|value| value.downcast_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the value with the given [`TypeId`] in this registration's
|
||||||
|
/// [type data].
|
||||||
|
///
|
||||||
|
/// Returns `None` if no such value exists.
|
||||||
|
///
|
||||||
|
/// For a static version of this method, see [`data_mut`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`data_mut`]: Self::data_mut
|
||||||
|
pub fn data_mut_by_id(&mut self, type_id: TypeId) -> Option<&mut dyn TypeData> {
|
||||||
|
self.data.get_mut(&type_id).map(DerefMut::deref_mut)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this registration contains the given [type data].
|
||||||
|
///
|
||||||
|
/// For a dynamic version of this method, see [`contains_by_id`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`contains_by_id`]: Self::contains_by_id
|
||||||
|
pub fn contains<T: TypeData>(&self) -> bool {
|
||||||
|
self.data.contains_key(&TypeId::of::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this registration contains the given [type data] with [`TypeId`].
|
||||||
|
///
|
||||||
|
/// For a static version of this method, see [`contains`].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
/// [`contains`]: Self::contains
|
||||||
|
pub fn contains_by_id(&self, type_id: TypeId) -> bool {
|
||||||
|
self.data.contains_key(&type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The total count of [type data] in this registration.
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this registration has no [type data].
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all [type data] in this registration.
|
||||||
|
///
|
||||||
|
/// The iterator yields a tuple of the [`TypeId`] and its corresponding type data.
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
pub fn iter(&self) -> impl ExactSizeIterator<Item = (TypeId, &dyn TypeData)> {
|
||||||
|
self.data.iter().map(|(id, data)| (*id, data.deref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable iterator over all [type data] in this registration.
|
||||||
|
///
|
||||||
|
/// The iterator yields a tuple of the [`TypeId`] and its corresponding type data.
|
||||||
|
///
|
||||||
|
/// [type data]: TypeData
|
||||||
|
pub fn iter_mut(&mut self) -> impl ExactSizeIterator<Item = (TypeId, &mut dyn TypeData)> {
|
||||||
|
self.data
|
||||||
|
.iter_mut()
|
||||||
|
.map(|(id, data)| (*id, data.deref_mut()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,11 +846,8 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{GetTypeRegistration, ReflectFromPtr};
|
use super::*;
|
||||||
use bevy_ptr::{Ptr, PtrMut};
|
|
||||||
|
|
||||||
use crate as bevy_reflect;
|
use crate as bevy_reflect;
|
||||||
use crate::Reflect;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reflect_from_ptr() {
|
fn test_reflect_from_ptr() {
|
||||||
@ -773,7 +862,7 @@ mod test {
|
|||||||
// not required in this situation because we no nobody messed with the TypeRegistry,
|
// not required in this situation because we no nobody messed with the TypeRegistry,
|
||||||
// but in the general case somebody could have replaced the ReflectFromPtr with an
|
// but in the general case somebody could have replaced the ReflectFromPtr with an
|
||||||
// instance for another type, so then we'd need to check that the type is the expected one
|
// instance for another type, so then we'd need to check that the type is the expected one
|
||||||
assert_eq!(reflect_from_ptr.type_id(), std::any::TypeId::of::<Foo>());
|
assert_eq!(reflect_from_ptr.type_id(), TypeId::of::<Foo>());
|
||||||
|
|
||||||
let mut value = Foo { a: 1.0 };
|
let mut value = Foo { a: 1.0 };
|
||||||
{
|
{
|
||||||
@ -804,4 +893,48 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_data_iter() {
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DataA(i32);
|
||||||
|
|
||||||
|
let mut registration = TypeRegistration::of::<Foo>();
|
||||||
|
registration.insert(DataA(123));
|
||||||
|
|
||||||
|
let mut iter = registration.iter();
|
||||||
|
|
||||||
|
let (id, data) = iter.next().unwrap();
|
||||||
|
assert_eq!(id, TypeId::of::<DataA>());
|
||||||
|
assert_eq!(data.downcast_ref::<DataA>().unwrap().0, 123);
|
||||||
|
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_data_iter_mut() {
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DataA(i32);
|
||||||
|
|
||||||
|
let mut registration = TypeRegistration::of::<Foo>();
|
||||||
|
registration.insert(DataA(123));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut iter = registration.iter_mut();
|
||||||
|
|
||||||
|
let (_, data) = iter.next().unwrap();
|
||||||
|
data.downcast_mut::<DataA>().unwrap().0 = 456;
|
||||||
|
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = registration.data::<DataA>().unwrap();
|
||||||
|
assert_eq!(data.0, 456);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user