Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 83 additions & 0 deletions crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs
Original file line number Diff line number Diff line change
@@ -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)?))
},
}
}
}
27 changes: 26 additions & 1 deletion crates/bevy_reflect/src/serde/de/deserializer.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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};
Expand Down Expand Up @@ -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());
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_reflect/src/serde/de/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading