mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 14:10:19 +00:00
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:
parent
f86ee32576
commit
eaa37f3b45
10 changed files with 509 additions and 70 deletions
|
@ -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
|
||||
|
|
|
@ -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)?))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ®istry);
|
||||
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(®istry);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
crates/bevy_reflect/src/serde/ser/custom_serialization.rs
Normal file
62
crates/bevy_reflect/src/serde/ser/custom_serialization.rs
Normal 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(),
|
||||
))))
|
||||
}
|
||||
}
|
|
@ -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"))]
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
100
crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs
Normal file
100
crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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")]
|
||||
|
|
Loading…
Reference in a new issue