bevy_reflect: Add DeserializeWithRegistry and SerializeWithRegistry (#8611)

# Objective

### The Problem

Currently, the reflection deserializers give little control to users for
how a type is deserialized. The most control a user can have is to
register `ReflectDeserialize`, which will use a type's `Deserialize`
implementation.

However, there are times when a type may require slightly more control.

For example, let's say we want to make Bevy's `Mesh` easier to
deserialize via reflection (assume `Mesh` actually implemented
`Reflect`). Since we want this to be extensible, we'll make it so users
can use their own types so long as they satisfy `Into<Mesh>`. The end
result should allow users to define a RON file like:

```rust
{
  "my_game::meshes::Sphere": (
    radius: 2.5
  )
}
```

### The Current Solution

Since we don't know the types ahead of time, we'll need to use
reflection. Luckily, we can access type information dynamically via the
type registry. Let's make a custom type data struct that users can
register on their types:

```rust
pub struct ReflectIntoMesh {
  // ...
}

impl<T: FromReflect + Into<Mesh>> FromType<T> for ReflectIntoMesh {
  fn from_type() -> Self {
    // ...
  }
}
```

Now we'll need a way to use this type data during deserialization.
Unfortunately, we can't use `Deserialize` since we need access to the
registry. This is where `DeserializeSeed` comes in handy:

```rust
pub struct MeshDeserializer<'a> {
  pub registry: &'a TypeRegistry
}

impl<'a, 'de> DeserializeSeed<'de> for MeshDeserializer<'a> {
  type Value = Mesh;

  fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
  where
    D: serde::Deserializer<'de>,
  {
    struct MeshVisitor<'a> {
      registry: &'a TypeRegistry
    }

    impl<'a, 'de> Visitor<'de> for MeshVisitor<'a> {
      fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
        write!(formatter, "map containing mesh information")
      }

      fn visit_map<A>(self, mut map: A) -> Result<Self::Value, serde:🇩🇪:Error> where A: MapAccess<'de> {
        // Parse the type name
        let type_name = map.next_key::<String>()?.unwrap();

        // Deserialize the value based on the type name
        let registration = self.registry
          .get_with_name(&type_name)
          .expect("should be registered");
        let value = map.next_value_seed(TypedReflectDeserializer {
          registration,
          registry: self.registry,
        })?;

        // Convert the deserialized value into a `Mesh`
        let into_mesh = registration.data::<ReflectIntoMesh>().unwrap();
        Ok(into_mesh.into(value))
      }
    }
  }
}
```

### The Problem with the Current Solution

The solution above works great when all we need to do is deserialize
`Mesh` directly. But now, we want to be able to deserialize a struct
like this:

```rust
struct Fireball {
  damage: f32,
  mesh: Mesh,
}
```

This might look simple enough and should theoretically be no problem for
the reflection deserializer to handle, but this is where our
`MeshDeserializer` solution starts to break down.

In order to use `MeshDeserializer`, we need to have access to the
registry. The reflection deserializers have access to that, but we have
no way of borrowing it for our own deserialization since they have no
way of knowing about `MeshDeserializer`.

This means we need to implement _another_ `DeserializeSeed`— this time
for `Fireball`!
And if we decided to put `Fireball` inside another type, well now we
need one for that type as well.

As you can see, this solution does not scale well and results in a lot
of unnecessary boilerplate for the user.

## Solution

> [!note]
> This PR originally only included the addition of
`DeserializeWithRegistry`. Since then, a corresponding
`SerializeWithRegistry` trait has also been added. The reasoning and
usage is pretty much the same as the former so I didn't bother to update
the full PR description.

Created the `DeserializeWithRegistry` trait and
`ReflectDeserializeWithRegistry` type data.

The `DeserializeWithRegistry` trait works like a standard `Deserialize`
but provides access to the registry. And by registering the
`ReflectDeserializeWithRegistry` type data, the reflection deserializers
will automatically use the `DeserializeWithRegistry` implementation,
just like it does for `Deserialize`.

All we need to do is make the following changes:

```diff
#[derive(Reflect)]
+ #[reflect(DeserializeWithRegistry)]
struct Mesh {
  // ...
}

- impl<'a, 'de> DeserializeSeed<'de> for MeshDeserializer<'a> {
-   type Value = Mesh;
-   fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ impl<'de> DeserializeWithRegistry<'de> for Mesh {
+   fn deserialize<D>(deserializer: D, registry: &TypeRegistry) -> Result<Self, D::Error>
    where
      D: serde::Deserializer<'de>,
    {
      // ...
    }
}
```

Now, any time the reflection deserializer comes across `Mesh`, it will
opt to use its `DeserializeWithRegistry` implementation. And this means
we no longer need to create a whole slew of `DeserializeSeed` types just
to deserialize `Mesh`.

### Why not a trait like `DeserializeSeed`?

While this would allow for anyone to define a deserializer for `Mesh`,
the problem is that it means __anyone can define a deserializer for
`Mesh`.__ This has the unfortunate consequence that users can never be
certain that their registration of `ReflectDeserializeSeed` is the one
that will actually be used.

We could consider adding something like that in the future, but I think
this PR's solution is much safer and follows the example set by
`ReflectDeserialize`.

### What if we made the `TypeRegistry` globally available?

This is one potential solution and has been discussed before (#6101).
However, that change is much more controversial and comes with its own
set of disadvantages (can't have multiple registries such as with
multiple worlds, likely some added performance cost with each access,
etc.).

### Followup Work

Once this PR is merged, we should consider merging `ReflectDeserialize`
into `DeserializeWithRegistry`. ~~There is already a blanket
implementation to make this transition generally pretty
straightforward.~~ The blanket implementations were removed for the sake
of this PR and will need to be re-added in the followup. I would propose
that we first mark `ReflectDeserialize` as deprecated, though, before we
outright remove it in a future release.

---

## Changelog

- Added the `DeserializeReflect` trait and `ReflectDeserializeReflect`
type data
- Added the `SerializeReflect` trait and `ReflectSerializeReflect` type
data
- Added `TypedReflectDeserializer::of` convenience constructor

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
This commit is contained in:
Gino Valente 2024-10-01 18:54:32 -07:00 committed by GitHub
parent f86ee32576
commit eaa37f3b45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 509 additions and 70 deletions

View file

@ -201,7 +201,7 @@ impl_reflect_opaque!(::core::num::NonZeroI8(
));
impl_reflect_opaque!(::core::num::Wrapping<T: Clone + Send + Sync>());
impl_reflect_opaque!(::core::num::Saturating<T: Clone + Send + Sync>());
impl_reflect_opaque!(::alloc::sync::Arc<T: Send + Sync>);
impl_reflect_opaque!(::alloc::sync::Arc<T: Send + Sync + ?Sized>);
// `Serialize` and `Deserialize` only for platforms supported by serde:
// https://github.com/serde-rs/serde/blob/3ffb86fc70efd3d329519e2dddfa306cc04f167c/serde/src/de/impls.rs#L1732

View file

@ -0,0 +1,83 @@
use crate::serde::de::error_utils::make_custom_error;
use crate::{FromType, PartialReflect, TypeRegistry};
use serde::Deserializer;
/// Trait used to provide finer control when deserializing a reflected type with one of
/// the reflection deserializers.
///
/// This trait is the reflection equivalent of `serde`'s [`Deserialize`] trait.
/// The main difference is that this trait provides access to the [`TypeRegistry`],
/// which means that we can use the registry and all its stored type information
/// to deserialize our type.
///
/// This can be useful when writing a custom reflection deserializer where we may
/// want to handle parts of the deserialization process, but temporarily pass control
/// to the standard reflection deserializer for other parts.
///
/// For the serialization equivalent of this trait, see [`SerializeWithRegistry`].
///
/// # Rationale
///
/// Without this trait and its associated [type data], such a deserializer would have to
/// write out all of the deserialization logic itself, possibly including
/// unnecessary code duplication and trivial implementations.
///
/// This is because a normal [`Deserialize`] implementation has no knowledge of the
/// [`TypeRegistry`] and therefore cannot create a reflection-based deserializer for
/// nested items.
///
/// # Implementors
///
/// In order for this to work with the reflection deserializers like [`TypedReflectDeserializer`]
/// and [`ReflectDeserializer`], implementors should be sure to register the
/// [`ReflectDeserializeWithRegistry`] type data.
/// This can be done [via the registry] or by adding `#[reflect(DeserializeWithRegistry)]` to
/// the type definition.
///
/// [`Deserialize`]: ::serde::Deserialize
/// [`SerializeWithRegistry`]: crate::serde::SerializeWithRegistry
/// [type data]: ReflectDeserializeWithRegistry
/// [`TypedReflectDeserializer`]: crate::serde::TypedReflectDeserializer
/// [`ReflectDeserializer`]: crate::serde::ReflectDeserializer
/// [via the registry]: TypeRegistry::register_type_data
pub trait DeserializeWithRegistry<'de>: PartialReflect + Sized {
fn deserialize<D>(deserializer: D, registry: &TypeRegistry) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}
/// Type data used to deserialize a [`PartialReflect`] type with a custom [`DeserializeWithRegistry`] implementation.
#[derive(Clone)]
pub struct ReflectDeserializeWithRegistry {
deserialize: fn(
deserializer: &mut dyn erased_serde::Deserializer,
registry: &TypeRegistry,
) -> Result<Box<dyn PartialReflect>, erased_serde::Error>,
}
impl ReflectDeserializeWithRegistry {
/// Deserialize a [`PartialReflect`] type with this type data's custom [`DeserializeWithRegistry`] implementation.
pub fn deserialize<'de, D>(
&self,
deserializer: D,
registry: &TypeRegistry,
) -> Result<Box<dyn PartialReflect>, D::Error>
where
D: Deserializer<'de>,
{
let mut erased = <dyn erased_serde::Deserializer>::erase(deserializer);
(self.deserialize)(&mut erased, registry).map_err(make_custom_error)
}
}
impl<T: PartialReflect + for<'de> DeserializeWithRegistry<'de>> FromType<T>
for ReflectDeserializeWithRegistry
{
fn from_type() -> Self {
Self {
deserialize: |deserializer, registry| {
Ok(Box::new(T::deserialize(deserializer, registry)?))
},
}
}
}

View file

@ -1,5 +1,6 @@
#[cfg(feature = "debug_stack")]
use crate::serde::de::error_utils::TYPE_INFO_STACK;
use crate::serde::ReflectDeserializeWithRegistry;
use crate::{
serde::{
de::{
@ -9,7 +10,7 @@ use crate::{
},
TypeRegistrationDeserializer,
},
PartialReflect, ReflectDeserialize, TypeInfo, TypeRegistration, TypeRegistry,
PartialReflect, ReflectDeserialize, TypeInfo, TypePath, TypeRegistration, TypeRegistry,
};
use core::{fmt, fmt::Formatter};
use serde::de::{DeserializeSeed, Error, IgnoredAny, MapAccess, Visitor};
@ -231,6 +232,7 @@ pub struct TypedReflectDeserializer<'a> {
}
impl<'a> TypedReflectDeserializer<'a> {
/// Creates a new [`TypedReflectDeserializer`] for the given type registration.
pub fn new(registration: &'a TypeRegistration, registry: &'a TypeRegistry) -> Self {
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.set(crate::type_info_stack::TypeInfoStack::new());
@ -241,6 +243,22 @@ impl<'a> TypedReflectDeserializer<'a> {
}
}
/// Creates a new [`TypedReflectDeserializer`] for the given type `T`.
///
/// # Panics
///
/// Panics if `T` is not registered in the given [`TypeRegistry`].
pub fn of<T: TypePath>(registry: &'a TypeRegistry) -> Self {
let registration = registry
.get(core::any::TypeId::of::<T>())
.unwrap_or_else(|| panic!("no registration found for type `{}`", T::type_path()));
Self {
registration,
registry,
}
}
/// An internal constructor for creating a deserializer without resetting the type info stack.
pub(super) fn new_internal(
registration: &'a TypeRegistration,
@ -269,6 +287,13 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> {
return Ok(value.into_partial_reflect());
}
if let Some(deserialize_reflect) =
self.registration.data::<ReflectDeserializeWithRegistry>()
{
let value = deserialize_reflect.deserialize(deserializer, self.registry)?;
return Ok(value);
}
match self.registration.type_info() {
TypeInfo::Struct(struct_info) => {
let mut dynamic_struct = deserializer.deserialize_struct(

View file

@ -1,7 +1,9 @@
pub use deserialize_with_registry::*;
pub use deserializer::*;
pub use registrations::*;
mod arrays;
mod deserialize_with_registry;
mod deserializer;
mod enums;
mod error_utils;

View file

@ -8,11 +8,10 @@ pub use type_data::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::{
self as bevy_reflect,
serde::{ReflectDeserializer, ReflectSerializer},
type_registry::TypeRegistry,
DynamicStruct, DynamicTupleStruct, FromReflect, PartialReflect, Reflect, Struct,
self as bevy_reflect, type_registry::TypeRegistry, DynamicStruct, DynamicTupleStruct,
FromReflect, PartialReflect, Reflect, Struct,
};
use serde::de::DeserializeSeed;
@ -183,4 +182,221 @@ mod tests {
.reflect_partial_eq(result.as_partial_reflect())
.unwrap());
}
mod type_data {
use super::*;
use crate::from_reflect::FromReflect;
use crate::serde::{DeserializeWithRegistry, ReflectDeserializeWithRegistry};
use crate::serde::{ReflectSerializeWithRegistry, SerializeWithRegistry};
use crate::{ReflectFromReflect, TypePath};
use alloc::sync::Arc;
use bevy_reflect_derive::reflect_trait;
use core::fmt::{Debug, Formatter};
use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserializer, Serializer};
#[reflect_trait]
trait Enemy: Reflect + Debug {
#[allow(dead_code, reason = "this method is purely for testing purposes")]
fn hp(&self) -> u8;
}
// This is needed to support Arc<dyn Enemy>
impl TypePath for dyn Enemy {
fn type_path() -> &'static str {
"dyn bevy_reflect::serde::tests::type_data::Enemy"
}
fn short_type_path() -> &'static str {
"dyn Enemy"
}
}
#[derive(Reflect, Debug)]
#[reflect(Enemy)]
struct Skeleton(u8);
impl Enemy for Skeleton {
fn hp(&self) -> u8 {
self.0
}
}
#[derive(Reflect, Debug)]
#[reflect(Enemy)]
struct Zombie {
health: u8,
walk_speed: f32,
}
impl Enemy for Zombie {
fn hp(&self) -> u8 {
self.health
}
}
#[derive(Reflect, Debug)]
struct Level {
name: String,
enemies: EnemyList,
}
#[derive(Reflect, Debug)]
#[reflect(SerializeWithRegistry, DeserializeWithRegistry)]
// Note that we have to use `Arc` instead of `Box` here due to the
// former being the only one between the two to implement `Reflect`.
struct EnemyList(Vec<Arc<dyn Enemy>>);
impl SerializeWithRegistry for EnemyList {
fn serialize<S>(
&self,
serializer: S,
registry: &TypeRegistry,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_seq(Some(self.0.len()))?;
for enemy in &self.0 {
state.serialize_element(&ReflectSerializer::new(
(**enemy).as_partial_reflect(),
registry,
))?;
}
state.end()
}
}
impl<'de> DeserializeWithRegistry<'de> for EnemyList {
fn deserialize<D>(deserializer: D, registry: &TypeRegistry) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EnemyListVisitor<'a> {
registry: &'a TypeRegistry,
}
impl<'a, 'de> Visitor<'de> for EnemyListVisitor<'a> {
type Value = Vec<Arc<dyn Enemy>>;
fn expecting(&self, formatter: &mut Formatter) -> core::fmt::Result {
write!(formatter, "a list of enemies")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut enemies = Vec::new();
while let Some(enemy) =
seq.next_element_seed(ReflectDeserializer::new(self.registry))?
{
let registration = self
.registry
.get_with_type_path(
enemy.get_represented_type_info().unwrap().type_path(),
)
.unwrap();
// 1. Convert any possible dynamic values to concrete ones
let enemy = registration
.data::<ReflectFromReflect>()
.unwrap()
.from_reflect(&*enemy)
.unwrap();
// 2. Convert the concrete value to a boxed trait object
let enemy = registration
.data::<ReflectEnemy>()
.unwrap()
.get_boxed(enemy)
.unwrap();
enemies.push(enemy.into());
}
Ok(enemies)
}
}
deserializer
.deserialize_seq(EnemyListVisitor { registry })
.map(EnemyList)
}
}
fn create_registry() -> TypeRegistry {
let mut registry = TypeRegistry::default();
registry.register::<Level>();
registry.register::<EnemyList>();
registry.register::<Skeleton>();
registry.register::<Zombie>();
registry
}
#[test]
fn should_serialize_with_serialize_with_registry() {
let registry = create_registry();
let level = Level {
name: String::from("Level 1"),
enemies: EnemyList(vec![
Arc::new(Skeleton(10)),
Arc::new(Zombie {
health: 20,
walk_speed: 0.5,
}),
]),
};
let serializer = ReflectSerializer::new(&level, &registry);
let serialized = ron::ser::to_string(&serializer).unwrap();
let expected = r#"{"bevy_reflect::serde::tests::type_data::Level":(name:"Level 1",enemies:[{"bevy_reflect::serde::tests::type_data::Skeleton":(10)},{"bevy_reflect::serde::tests::type_data::Zombie":(health:20,walk_speed:0.5)}])}"#;
assert_eq!(expected, serialized);
}
#[test]
fn should_deserialize_with_deserialize_with_registry() {
let registry = create_registry();
let input = r#"{"bevy_reflect::serde::tests::type_data::Level":(name:"Level 1",enemies:[{"bevy_reflect::serde::tests::type_data::Skeleton":(10)},{"bevy_reflect::serde::tests::type_data::Zombie":(health:20,walk_speed:0.5)}])}"#;
let mut deserializer = ron::de::Deserializer::from_str(input).unwrap();
let reflect_deserializer = ReflectDeserializer::new(&registry);
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
let output = Level::from_reflect(&*value).unwrap();
let expected = Level {
name: String::from("Level 1"),
enemies: EnemyList(vec![
Arc::new(Skeleton(10)),
Arc::new(Zombie {
health: 20,
walk_speed: 0.5,
}),
]),
};
// Poor man's comparison since we can't derive PartialEq for Arc<dyn Enemy>
assert_eq!(format!("{:?}", expected), format!("{:?}", output));
let unexpected = Level {
name: String::from("Level 1"),
enemies: EnemyList(vec![
Arc::new(Skeleton(20)),
Arc::new(Zombie {
health: 20,
walk_speed: 5.0,
}),
]),
};
// Poor man's comparison since we can't derive PartialEq for Arc<dyn Enemy>
assert_ne!(format!("{:?}", unexpected), format!("{:?}", output));
}
}
}

View file

@ -0,0 +1,62 @@
use crate::serde::ser::error_utils::make_custom_error;
#[cfg(feature = "debug_stack")]
use crate::serde::ser::error_utils::TYPE_INFO_STACK;
use crate::serde::ReflectSerializeWithRegistry;
use crate::{PartialReflect, ReflectSerialize, TypeRegistry};
use core::borrow::Borrow;
use serde::{Serialize, Serializer};
/// Attempts to serialize a [`PartialReflect`] value with custom [`ReflectSerialize`]
/// or [`ReflectSerializeWithRegistry`] type data.
///
/// On success, returns the result of the serialization.
/// On failure, returns the original serializer and the error that occurred.
pub(super) fn try_custom_serialize<S: Serializer>(
value: &dyn PartialReflect,
type_registry: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S::Error>, (S, S::Error)> {
let Some(value) = value.try_as_reflect() else {
return Err((
serializer,
make_custom_error(format_args!(
"type `{}` does not implement `Reflect`",
value.reflect_type_path()
)),
));
};
let info = value.reflect_type_info();
let Some(registration) = type_registry.get(info.type_id()) else {
return Err((
serializer,
make_custom_error(format_args!(
"type `{}` is not registered in the type registry",
info.type_path(),
)),
));
};
if let Some(reflect_serialize) = registration.data::<ReflectSerialize>() {
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.with_borrow_mut(crate::type_info_stack::TypeInfoStack::pop);
Ok(reflect_serialize
.get_serializable(value)
.borrow()
.serialize(serializer))
} else if let Some(reflect_serialize_with_registry) =
registration.data::<ReflectSerializeWithRegistry>()
{
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.with_borrow_mut(crate::type_info_stack::TypeInfoStack::pop);
Ok(reflect_serialize_with_registry.serialize(value, serializer, type_registry))
} else {
Err((serializer, make_custom_error(format_args!(
"type `{}` did not register the `ReflectSerialize` or `ReflectSerializeWithRegistry` type data. For certain types, this may need to be registered manually using `register_type_data`",
info.type_path(),
))))
}
}

View file

@ -1,12 +1,15 @@
pub use serializable::*;
pub use serialize_with_registry::*;
pub use serializer::*;
mod arrays;
mod custom_serialization;
mod enums;
mod error_utils;
mod lists;
mod maps;
mod serializable;
mod serialize_with_registry;
mod serializer;
mod sets;
mod structs;
@ -459,7 +462,7 @@ mod tests {
assert_eq!(
error,
ron::Error::Message(
"type `core::ops::RangeInclusive<f32>` did not register the `ReflectSerialize` type data. For certain types, this may need to be registered manually using `register_type_data` (stack: `core::ops::RangeInclusive<f32>`)".to_string()
"type `core::ops::RangeInclusive<f32>` did not register the `ReflectSerialize` or `ReflectSerializeWithRegistry` type data. For certain types, this may need to be registered manually using `register_type_data` (stack: `core::ops::RangeInclusive<f32>`)".to_string()
)
);
#[cfg(not(feature = "debug_stack"))]

View file

@ -1,8 +1,4 @@
use crate::{
serde::ser::error_utils::make_custom_error, PartialReflect, ReflectSerialize, TypeRegistry,
};
use core::ops::Deref;
use serde::ser::Error;
/// A type-erased serializable value.
pub enum Serializable<'a> {
@ -10,47 +6,6 @@ pub enum Serializable<'a> {
Borrowed(&'a dyn erased_serde::Serialize),
}
impl<'a> Serializable<'a> {
/// Attempts to create a [`Serializable`] from a [`PartialReflect`] value.
///
/// Returns an error if any of the following conditions are met:
/// - The underlying type of `value` does not implement [`Reflect`].
/// - The underlying type of `value` does not represent any type (via [`PartialReflect::get_represented_type_info`]).
/// - The represented type of `value` is not registered in the `type_registry`.
/// - The represented type of `value` did not register the [`ReflectSerialize`] type data.
///
/// [`Reflect`]: crate::Reflect
pub fn try_from_reflect_value<E: Error>(
value: &'a dyn PartialReflect,
type_registry: &TypeRegistry,
) -> Result<Serializable<'a>, E> {
let value = value.try_as_reflect().ok_or_else(|| {
make_custom_error(format_args!(
"type `{}` does not implement `Reflect`",
value.reflect_type_path()
))
})?;
let info = value.reflect_type_info();
let registration = type_registry.get(info.type_id()).ok_or_else(|| {
make_custom_error(format_args!(
"type `{}` is not registered in the type registry",
info.type_path(),
))
})?;
let reflect_serialize = registration.data::<ReflectSerialize>().ok_or_else(|| {
make_custom_error(format_args!(
"type `{}` did not register the `ReflectSerialize` type data. For certain types, this may need to be registered manually using `register_type_data`",
info.type_path(),
))
})?;
Ok(reflect_serialize.get_serializable(value))
}
}
impl<'a> Deref for Serializable<'a> {
type Target = dyn erased_serde::Serialize + 'a;

View file

@ -0,0 +1,100 @@
use crate::{FromType, Reflect, TypeRegistry};
use serde::{Serialize, Serializer};
/// Trait used to provide finer control when serializing a reflected type with one of
/// the reflection serializers.
///
/// This trait is the reflection equivalent of `serde`'s [`Serialize`] trait.
/// The main difference is that this trait provides access to the [`TypeRegistry`],
/// which means that we can use the registry and all its stored type information
/// to serialize our type.
///
/// This can be useful when writing a custom reflection serializer where we may
/// want to handle parts of the serialization process, but temporarily pass control
/// to the standard reflection serializer for other parts.
///
/// For the deserialization equivalent of this trait, see [`DeserializeWithRegistry`].
///
/// # Rationale
///
/// Without this trait and its associated [type data], such a serializer would have to
/// write out all of the serialization logic itself, possibly including
/// unnecessary code duplication and trivial implementations.
///
/// This is because a normal [`Serialize`] implementation has no knowledge of the
/// [`TypeRegistry`] and therefore cannot create a reflection-based serializer for
/// nested items.
///
/// # Implementors
///
/// In order for this to work with the reflection serializers like [`TypedReflectSerializer`]
/// and [`ReflectSerializer`], implementors should be sure to register the
/// [`ReflectSerializeWithRegistry`] type data.
/// This can be done [via the registry] or by adding `#[reflect(SerializeWithRegistry)]` to
/// the type definition.
///
/// [`DeserializeWithRegistry`]: crate::serde::DeserializeWithRegistry
/// [type data]: ReflectSerializeWithRegistry
/// [`TypedReflectSerializer`]: crate::serde::TypedReflectSerializer
/// [`ReflectSerializer`]: crate::serde::ReflectSerializer
/// [via the registry]: TypeRegistry::register_type_data
pub trait SerializeWithRegistry {
fn serialize<S>(&self, serializer: S, registry: &TypeRegistry) -> Result<S::Ok, S::Error>
where
S: Serializer;
}
/// Type data used to serialize a [`Reflect`] type with a custom [`SerializeWithRegistry`] implementation.
#[derive(Clone)]
pub struct ReflectSerializeWithRegistry {
serialize: for<'a> fn(
value: &'a dyn Reflect,
registry: &'a TypeRegistry,
) -> Box<dyn erased_serde::Serialize + 'a>,
}
impl ReflectSerializeWithRegistry {
/// Serialize a [`Reflect`] type with this type data's custom [`SerializeWithRegistry`] implementation.
pub fn serialize<S>(
&self,
value: &dyn Reflect,
serializer: S,
registry: &TypeRegistry,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
((self.serialize)(value, registry)).serialize(serializer)
}
}
impl<T: Reflect + SerializeWithRegistry> FromType<T> for ReflectSerializeWithRegistry {
fn from_type() -> Self {
Self {
serialize: |value: &dyn Reflect, registry| {
let value = value.downcast_ref::<T>().unwrap_or_else(|| {
panic!(
"Expected value to be of type {:?} but received {:?}",
core::any::type_name::<T>(),
value.reflect_type_path()
)
});
Box::new(SerializableWithRegistry { value, registry })
},
}
}
}
struct SerializableWithRegistry<'a, T: SerializeWithRegistry> {
value: &'a T,
registry: &'a TypeRegistry,
}
impl<'a, T: SerializeWithRegistry> Serialize for SerializableWithRegistry<'a, T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.value.serialize(serializer, self.registry)
}
}

View file

@ -1,14 +1,11 @@
#[cfg(feature = "debug_stack")]
use crate::serde::ser::error_utils::TYPE_INFO_STACK;
use crate::{
serde::{
ser::{
arrays::ArraySerializer, enums::EnumSerializer, error_utils::make_custom_error,
lists::ListSerializer, maps::MapSerializer, sets::SetSerializer,
structs::StructSerializer, tuple_structs::TupleStructSerializer,
tuples::TupleSerializer,
},
Serializable,
serde::ser::{
arrays::ArraySerializer, custom_serialization::try_custom_serialize, enums::EnumSerializer,
error_utils::make_custom_error, lists::ListSerializer, maps::MapSerializer,
sets::SetSerializer, structs::StructSerializer, tuple_structs::TupleStructSerializer,
tuples::TupleSerializer,
},
PartialReflect, ReflectRef, TypeRegistry,
};
@ -158,16 +155,12 @@ impl<'a> Serialize for TypedReflectSerializer<'a> {
TYPE_INFO_STACK.with_borrow_mut(|stack| stack.push(info));
}
}
// Handle both Value case and types that have a custom `Serialize`
let serializable =
Serializable::try_from_reflect_value::<S::Error>(self.value, self.registry);
if let Ok(serializable) = serializable {
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.with_borrow_mut(crate::type_info_stack::TypeInfoStack::pop);
return serializable.serialize(serializer);
}
let (serializer, error) = match try_custom_serialize(self.value, self.registry, serializer)
{
Ok(result) => return result,
Err(value) => value,
};
let output = match self.value.reflect_ref() {
ReflectRef::Struct(value) => {
@ -196,7 +189,7 @@ impl<'a> Serialize for TypedReflectSerializer<'a> {
}
#[cfg(feature = "functions")]
ReflectRef::Function(_) => Err(make_custom_error("functions cannot be serialized")),
ReflectRef::Opaque(_) => Err(serializable.err().unwrap()),
ReflectRef::Opaque(_) => Err(error),
};
#[cfg(feature = "debug_stack")]