Skip to content

Commit

Permalink
bevy_reflect: Add dynamic type data access and iteration to `TypeRegi…
Browse files Browse the repository at this point in the history
…stration` (#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());
```
  • Loading branch information
MrGVSV authored Sep 23, 2024
1 parent 55dddaf commit 51accd3
Showing 1 changed file with 156 additions and 23 deletions.
179 changes: 156 additions & 23 deletions crates/bevy_reflect/src/type_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::{HashMap, HashSet, TypeIdMap};
use downcast_rs::{impl_downcast, Downcast};
use serde::Deserialize;
use std::ops::{Deref, DerefMut};
use std::{
any::TypeId,
fmt::Debug,
Expand Down Expand Up @@ -478,51 +479,142 @@ impl Debug for 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.
///
#[inline]
pub fn type_id(&self) -> TypeId {
self.type_info.type_id()
}

/// Returns a reference to the value of type `T` in this registration's type
/// data.
/// Returns a reference to the registration's [`TypeInfo`]
pub fn type_info(&self) -> &'static TypeInfo {
self.type_info
}

/// Inserts an instance of `T` into this registration's [type data].
///
/// If another instance of `T` was previously inserted, it is replaced.
///
/// [type data]: TypeData
pub fn insert<T: TypeData>(&mut self, data: T) {
self.data.insert(TypeId::of::<T>(), Box::new(data));
}

/// Returns a 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_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 mutable reference to the value of type `T` in this
/// registration's type data.
/// 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 reference to the registration's [`TypeInfo`]
pub fn type_info(&self) -> &'static TypeInfo {
self.type_info
/// 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)
}

/// Inserts an instance of `T` into this registration's type data.
/// Returns true if this registration contains the given [type data].
///
/// If another instance of `T` was previously inserted, it is replaced.
pub fn insert<T: TypeData>(&mut self, data: T) {
self.data.insert(TypeId::of::<T>(), Box::new(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>())
}

/// Creates type registration information for `T`.
pub fn of<T: Reflect + Typed + TypePath>() -> Self {
Self {
data: Default::default(),
type_info: T::type_info(),
}
/// 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()))
}
}

Expand Down Expand Up @@ -754,11 +846,8 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
#[cfg(test)]
#[allow(unsafe_code)]
mod test {
use crate::{GetTypeRegistration, ReflectFromPtr};
use bevy_ptr::{Ptr, PtrMut};

use super::*;
use crate as bevy_reflect;
use crate::Reflect;

#[test]
fn test_reflect_from_ptr() {
Expand All @@ -773,7 +862,7 @@ mod test {
// 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
// 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 };
{
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 51accd3

Please sign in to comment.