bevy_reflect: Add ReflectSerializerProcessor (#15548)

**NOTE: This is based on, and should be merged alongside,
https://github.com/bevyengine/bevy/pull/15482.** I'll leave this in
draft until that PR is merged.

# Objective

Equivalent of https://github.com/bevyengine/bevy/pull/15482 but for
serialization. See that issue for the motivation.

Also part of this tracking issue:
https://github.com/bevyengine/bevy/issues/15518

This PR is non-breaking, just like the deserializer PR (because the new
type parameter `P` has a default `P = ()`).

## Solution

Identical solution to the deserializer PR.

## Testing

Added unit tests and a very comprehensive doc test outlining a clear
example and use case.
This commit is contained in:
aecsocket 2024-11-17 14:05:39 +00:00 committed by GitHub
parent ded5ce27ae
commit 17c4b070ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 628 additions and 138 deletions

View file

@ -15,6 +15,8 @@ use crate::{PartialReflect, TypeRegistration, TypeRegistry};
/// deserializer and give back a [`Box<dyn PartialReflect>`], or return
/// ownership of the deserializer back, and continue with the default logic.
///
/// The serialization equivalent of this is [`ReflectSerializerProcessor`].
///
/// # Compared to [`DeserializeWithRegistry`]
///
/// [`DeserializeWithRegistry`] allows you to define how your type will be
@ -134,6 +136,7 @@ use crate::{PartialReflect, TypeRegistration, TypeRegistry};
/// [`TypedReflectDeserializer`]: crate::serde::TypedReflectDeserializer
/// [`try_deserialize`]: Self::try_deserialize
/// [`DeserializeWithRegistry`]: crate::serde::DeserializeWithRegistry
/// [`ReflectSerializerProcessor`]: crate::serde::ReflectSerializerProcessor
pub trait ReflectDeserializerProcessor {
/// Attempts to deserialize the value which a [`TypedReflectDeserializer`]
/// is currently looking at, and knows the type of.

View file

@ -1,26 +1,27 @@
use crate::{serde::TypedReflectSerializer, Array, TypeRegistry};
use serde::{ser::SerializeTuple, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`Array`] values.
pub(super) struct ArraySerializer<'a> {
array: &'a dyn Array,
registry: &'a TypeRegistry,
pub(super) struct ArraySerializer<'a, P> {
pub array: &'a dyn Array,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> ArraySerializer<'a> {
pub fn new(array: &'a dyn Array, registry: &'a TypeRegistry) -> Self {
Self { array, registry }
}
}
impl<'a> Serialize for ArraySerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for ArraySerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_tuple(self.array.len())?;
for value in self.array.iter() {
state.serialize_element(&TypedReflectSerializer::new_internal(value, self.registry))?;
state.serialize_element(&TypedReflectSerializer::new_internal(
value,
self.registry,
self.processor,
))?;
}
state.end()
}

View file

@ -7,22 +7,16 @@ use serde::{
Serialize,
};
use super::ReflectSerializerProcessor;
/// A serializer for [`Enum`] values.
pub(super) struct EnumSerializer<'a> {
enum_value: &'a dyn Enum,
registry: &'a TypeRegistry,
pub(super) struct EnumSerializer<'a, P> {
pub enum_value: &'a dyn Enum,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> EnumSerializer<'a> {
pub fn new(enum_value: &'a dyn Enum, registry: &'a TypeRegistry) -> Self {
Self {
enum_value,
registry,
}
}
}
impl<'a> Serialize for EnumSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for EnumSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -86,7 +80,11 @@ impl<'a> Serialize for EnumSerializer<'a> {
let field_info = struct_info.field_at(index).unwrap();
state.serialize_field(
field_info.name(),
&TypedReflectSerializer::new_internal(field.value(), self.registry),
&TypedReflectSerializer::new_internal(
field.value(),
self.registry,
self.processor,
),
)?;
}
state.end()
@ -97,14 +95,17 @@ impl<'a> Serialize for EnumSerializer<'a> {
if type_info.type_path_table().module_path() == Some("core::option")
&& type_info.type_path_table().ident() == Some("Option")
{
serializer
.serialize_some(&TypedReflectSerializer::new_internal(field, self.registry))
serializer.serialize_some(&TypedReflectSerializer::new_internal(
field,
self.registry,
self.processor,
))
} else {
serializer.serialize_newtype_variant(
enum_name,
variant_index,
variant_name,
&TypedReflectSerializer::new_internal(field, self.registry),
&TypedReflectSerializer::new_internal(field, self.registry, self.processor),
)
}
}
@ -119,6 +120,7 @@ impl<'a> Serialize for EnumSerializer<'a> {
state.serialize_field(&TypedReflectSerializer::new_internal(
field.value(),
self.registry,
self.processor,
))?;
}
state.end()

View file

@ -1,26 +1,27 @@
use crate::{serde::TypedReflectSerializer, List, TypeRegistry};
use serde::{ser::SerializeSeq, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`List`] values.
pub(super) struct ListSerializer<'a> {
list: &'a dyn List,
registry: &'a TypeRegistry,
pub(super) struct ListSerializer<'a, P> {
pub list: &'a dyn List,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> ListSerializer<'a> {
pub fn new(list: &'a dyn List, registry: &'a TypeRegistry) -> Self {
Self { list, registry }
}
}
impl<'a> Serialize for ListSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for ListSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_seq(Some(self.list.len()))?;
for value in self.list.iter() {
state.serialize_element(&TypedReflectSerializer::new_internal(value, self.registry))?;
state.serialize_element(&TypedReflectSerializer::new_internal(
value,
self.registry,
self.processor,
))?;
}
state.end()
}

View file

@ -1,19 +1,16 @@
use crate::{serde::TypedReflectSerializer, Map, TypeRegistry};
use serde::{ser::SerializeMap, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`Map`] values.
pub(super) struct MapSerializer<'a> {
map: &'a dyn Map,
registry: &'a TypeRegistry,
pub(super) struct MapSerializer<'a, P> {
pub map: &'a dyn Map,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> MapSerializer<'a> {
pub fn new(map: &'a dyn Map, registry: &'a TypeRegistry) -> Self {
Self { map, registry }
}
}
impl<'a> Serialize for MapSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for MapSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -21,8 +18,8 @@ impl<'a> Serialize for MapSerializer<'a> {
let mut state = serializer.serialize_map(Some(self.map.len()))?;
for (key, value) in self.map.iter() {
state.serialize_entry(
&TypedReflectSerializer::new_internal(key, self.registry),
&TypedReflectSerializer::new_internal(value, self.registry),
&TypedReflectSerializer::new_internal(key, self.registry, self.processor),
&TypedReflectSerializer::new_internal(value, self.registry, self.processor),
)?;
}
state.end()

View file

@ -1,3 +1,4 @@
pub use processor::*;
pub use serializable::*;
pub use serialize_with_registry::*;
pub use serializer::*;
@ -8,6 +9,7 @@ mod enums;
mod error_utils;
mod lists;
mod maps;
mod processor;
mod serializable;
mod serialize_with_registry;
mod serializer;
@ -19,13 +21,15 @@ mod tuples;
#[cfg(test)]
mod tests {
use crate::{
self as bevy_reflect, serde::ReflectSerializer, PartialReflect, Reflect, ReflectSerialize,
Struct, TypeRegistry,
self as bevy_reflect,
serde::{ReflectSerializer, ReflectSerializerProcessor},
PartialReflect, Reflect, ReflectSerialize, Struct, TypeRegistry,
};
use bevy_utils::{HashMap, HashSet};
use core::any::TypeId;
use core::{f32::consts::PI, ops::RangeInclusive};
use ron::{extensions::Extensions, ser::PrettyConfig};
use serde::Serialize;
use serde::{Serialize, Serializer};
#[derive(Reflect, Debug, PartialEq)]
struct MyStruct {
@ -474,6 +478,172 @@ mod tests {
);
}
#[test]
fn should_use_processor_for_custom_serialization() {
#[derive(Reflect, Debug, PartialEq)]
struct Foo {
bar: i32,
qux: i64,
}
struct FooProcessor;
impl ReflectSerializerProcessor for FooProcessor {
fn try_serialize<S>(
&self,
value: &dyn PartialReflect,
_: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S>, S::Error>
where
S: Serializer,
{
let Some(value) = value.try_as_reflect() else {
return Ok(Err(serializer));
};
let type_id = value.reflect_type_info().type_id();
if type_id == TypeId::of::<i64>() {
Ok(Ok(serializer.serialize_str("custom!")?))
} else {
Ok(Err(serializer))
}
}
}
let value = Foo { bar: 123, qux: 456 };
let mut registry = TypeRegistry::new();
registry.register::<Foo>();
let processor = FooProcessor;
let serializer = ReflectSerializer::with_processor(&value, &registry, &processor);
let config = PrettyConfig::default().new_line(String::from("\n"));
let output = ron::ser::to_string_pretty(&serializer, config).unwrap();
let expected = r#"{
"bevy_reflect::serde::ser::tests::Foo": (
bar: 123,
qux: "custom!",
),
}"#;
assert_eq!(expected, output);
}
#[test]
fn should_use_processor_for_multiple_registrations() {
#[derive(Reflect, Debug, PartialEq)]
struct Foo {
bar: i32,
sub: SubFoo,
}
#[derive(Reflect, Debug, PartialEq)]
struct SubFoo {
val: i32,
}
struct FooProcessor;
impl ReflectSerializerProcessor for FooProcessor {
fn try_serialize<S>(
&self,
value: &dyn PartialReflect,
_: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S>, S::Error>
where
S: Serializer,
{
let Some(value) = value.try_as_reflect() else {
return Ok(Err(serializer));
};
let type_id = value.reflect_type_info().type_id();
if type_id == TypeId::of::<i32>() {
Ok(Ok(serializer.serialize_str("an i32")?))
} else if type_id == TypeId::of::<SubFoo>() {
Ok(Ok(serializer.serialize_str("a SubFoo")?))
} else {
Ok(Err(serializer))
}
}
}
let value = Foo {
bar: 123,
sub: SubFoo { val: 456 },
};
let mut registry = TypeRegistry::new();
registry.register::<Foo>();
registry.register::<SubFoo>();
let processor = FooProcessor;
let serializer = ReflectSerializer::with_processor(&value, &registry, &processor);
let config = PrettyConfig::default().new_line(String::from("\n"));
let output = ron::ser::to_string_pretty(&serializer, config).unwrap();
let expected = r#"{
"bevy_reflect::serde::ser::tests::Foo": (
bar: "an i32",
sub: "a SubFoo",
),
}"#;
assert_eq!(expected, output);
}
#[test]
fn should_propagate_processor_serialize_error() {
struct ErroringProcessor;
impl ReflectSerializerProcessor for ErroringProcessor {
fn try_serialize<S>(
&self,
value: &dyn PartialReflect,
_: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S>, S::Error>
where
S: Serializer,
{
let Some(value) = value.try_as_reflect() else {
return Ok(Err(serializer));
};
let type_id = value.reflect_type_info().type_id();
if type_id == TypeId::of::<i32>() {
Err(serde::ser::Error::custom("my custom serialize error"))
} else {
Ok(Err(serializer))
}
}
}
let value = 123_i32;
let registry = TypeRegistry::new();
let processor = ErroringProcessor;
let serializer = ReflectSerializer::with_processor(&value, &registry, &processor);
let error = ron::ser::to_string_pretty(&serializer, PrettyConfig::default()).unwrap_err();
#[cfg(feature = "debug_stack")]
assert_eq!(
error,
ron::Error::Message("my custom serialize error (stack: `i32`)".to_string())
);
#[cfg(not(feature = "debug_stack"))]
assert_eq!(
error,
ron::Error::Message("my custom serialize error".to_string())
);
}
#[cfg(feature = "functions")]
mod functions {
use super::*;

View file

@ -0,0 +1,196 @@
use serde::Serializer;
use crate::{PartialReflect, TypeRegistry};
/// Allows overriding the default serialization behavior of
/// [`ReflectSerializer`] and [`TypedReflectSerializer`] for specific values.
///
/// When serializing a reflected value, you may want to override the default
/// behavior and use your own logic for serialization. This logic may also be
/// context-dependent, and only apply for a single use of your
/// [`ReflectSerializer`]. To achieve this, you can create a processor and pass
/// it into your serializer.
///
/// Whenever the serializer attempts to serialize a value, it will first call
/// [`try_serialize`] on your processor, which may take ownership of the
/// serializer and write into the serializer (successfully or not), or return
/// ownership of the serializer back, and continue with the default logic.
///
/// The deserialization equivalent of this is [`ReflectDeserializerProcessor`].
///
/// # Compared to [`SerializeWithRegistry`]
///
/// [`SerializeWithRegistry`] allows you to define how your type will be
/// serialized by a [`TypedReflectSerializer`], given the extra context of the
/// [`TypeRegistry`]. If your type can be serialized entirely using that, then
/// you should prefer implementing that trait instead of using a processor.
///
/// However, you may need more context-dependent data which is only present in
/// the scope where you create the [`TypedReflectSerializer`]. For example, if
/// you need to use a reference to a value while serializing, then there is no
/// way to do this with [`SerializeWithRegistry`] as you can't pass that
/// reference into anywhere. This is where a processor is useful, as the
/// processor can capture local variables.
///
/// A [`ReflectSerializerProcessor`] always takes priority over a
/// [`SerializeWithRegistry`] implementation, so this is also useful for
/// overriding serialization behavior if you need to do something custom.
///
/// # Examples
///
/// Serializing a reflected value when saving an asset to disk, and replacing
/// asset handles with the handle path (if it has one):
///
/// ```
/// # use core::any::Any;
/// # use serde::Serialize;
/// # use bevy_reflect::{PartialReflect, Reflect, TypeData, TypeRegistry};
/// # use bevy_reflect::serde::{ReflectSerializer, ReflectSerializerProcessor};
/// #
/// # #[derive(Debug, Clone, Reflect)]
/// # struct Handle<T>(T);
/// # #[derive(Debug, Clone, Reflect)]
/// # struct Mesh;
/// #
/// # struct ReflectHandle;
/// # impl TypeData for ReflectHandle {
/// # fn clone_type_data(&self) -> Box<dyn TypeData> {
/// # unimplemented!()
/// # }
/// # }
/// # impl ReflectHandle {
/// # fn downcast_handle_untyped(&self, handle: &(dyn Any + 'static)) -> Option<UntypedHandle> {
/// # unimplemented!()
/// # }
/// # }
/// #
/// # #[derive(Debug, Clone)]
/// # struct UntypedHandle;
/// # impl UntypedHandle {
/// # fn path(&self) -> Option<&str> {
/// # unimplemented!()
/// # }
/// # }
/// # type AssetError = Box<dyn core::error::Error>;
/// #
/// #[derive(Debug, Clone, Reflect)]
/// struct MyAsset {
/// name: String,
/// mesh: Handle<Mesh>,
/// }
///
/// struct HandleProcessor;
///
/// impl ReflectSerializerProcessor for HandleProcessor {
/// fn try_serialize<S>(
/// &self,
/// value: &dyn PartialReflect,
/// registry: &TypeRegistry,
/// serializer: S,
/// ) -> Result<Result<S::Ok, S>, S::Error>
/// where
/// S: serde::Serializer,
/// {
/// let Some(value) = value.try_as_reflect() else {
/// // we don't have any info on this type; do the default serialization logic
/// return Ok(Err(serializer));
/// };
/// let type_id = value.reflect_type_info().type_id();
/// let Some(reflect_handle) = registry.get_type_data::<ReflectHandle>(type_id) else {
/// // this isn't a `Handle<T>`
/// return Ok(Err(serializer));
/// };
///
/// let untyped_handle = reflect_handle
/// .downcast_handle_untyped(value.as_any())
/// .unwrap();
/// if let Some(path) = untyped_handle.path() {
/// Ok(Ok(serializer.serialize_str(path)?))
/// } else {
/// Ok(Ok(serializer.serialize_unit()?))
/// }
/// }
/// }
///
/// fn save(type_registry: &TypeRegistry, asset: &MyAsset) -> Result<Vec<u8>, AssetError> {
/// let mut asset_bytes = Vec::new();
///
/// let processor = HandleProcessor;
/// let serializer = ReflectSerializer::with_processor(asset, type_registry, &processor);
/// let mut ron_serializer = ron::Serializer::new(&mut asset_bytes, None)?;
///
/// serializer.serialize(&mut ron_serializer)?;
/// Ok(asset_bytes)
/// }
/// ```
///
/// [`ReflectSerializer`]: crate::serde::ReflectSerializer
/// [`TypedReflectSerializer`]: crate::serde::TypedReflectSerializer
/// [`try_serialize`]: Self::try_serialize
/// [`SerializeWithRegistry`]: crate::serde::SerializeWithRegistry
/// [`ReflectDeserializerProcessor`]: crate::serde::ReflectDeserializerProcessor
pub trait ReflectSerializerProcessor {
/// Attempts to serialize the value which a [`TypedReflectSerializer`] is
/// currently looking at.
///
/// If you want to override the default serialization, return
/// `Ok(Ok(value))` with an `Ok` output from the serializer.
///
/// If you don't want to override the serialization, return ownership of
/// the serializer back via `Ok(Err(serializer))`.
///
/// You can use the type registry to read info about the type you're
/// serializing, or just try to downcast the value directly:
///
/// ```
/// # use bevy_reflect::{TypeRegistration, TypeRegistry, PartialReflect};
/// # use bevy_reflect::serde::ReflectSerializerProcessor;
/// # use core::any::TypeId;
/// struct I32AsStringProcessor;
///
/// impl ReflectSerializerProcessor for I32AsStringProcessor {
/// fn try_serialize<S>(
/// &self,
/// value: &dyn PartialReflect,
/// registry: &TypeRegistry,
/// serializer: S,
/// ) -> Result<Result<S::Ok, S>, S::Error>
/// where
/// S: serde::Serializer
/// {
/// if let Some(value) = value.try_downcast_ref::<i32>() {
/// let value_as_string = format!("{value:?}");
/// Ok(Ok(serializer.serialize_str(&value_as_string)?))
/// } else {
/// // Not an `i32`, just do the default serialization
/// Ok(Err(serializer))
/// }
/// }
/// }
/// ```
///
/// [`TypedReflectSerializer`]: crate::serde::TypedReflectSerializer
/// [`Reflect`]: crate::Reflect
fn try_serialize<S>(
&self,
value: &dyn PartialReflect,
registry: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S>, S::Error>
where
S: Serializer;
}
impl ReflectSerializerProcessor for () {
fn try_serialize<S>(
&self,
_value: &dyn PartialReflect,
_registry: &TypeRegistry,
serializer: S,
) -> Result<Result<S::Ok, S>, S::Error>
where
S: Serializer,
{
Ok(Err(serializer))
}
}

View file

@ -9,7 +9,9 @@ use crate::{
},
PartialReflect, ReflectRef, TypeRegistry,
};
use serde::{ser::SerializeMap, Serialize};
use serde::{ser::SerializeMap, Serialize, Serializer};
use super::ReflectSerializerProcessor;
/// A general purpose serializer for reflected types.
///
@ -23,6 +25,10 @@ use serde::{ser::SerializeMap, Serialize};
/// where the key is the _full_ [type path] of the reflected type
/// and the value is the serialized data.
///
/// If you want to override serialization for specific values, you can pass in
/// a reference to a [`ReflectSerializerProcessor`] which will take priority
/// over all other serialization methods - see [`with_processor`].
///
/// # Example
///
/// ```
@ -47,21 +53,53 @@ use serde::{ser::SerializeMap, Serialize};
///
/// [`ReflectDeserializer`]: crate::serde::ReflectDeserializer
/// [type path]: crate::TypePath::type_path
pub struct ReflectSerializer<'a> {
/// [`with_processor`]: Self::with_processor
pub struct ReflectSerializer<'a, P = ()> {
value: &'a dyn PartialReflect,
registry: &'a TypeRegistry,
processor: Option<&'a P>,
}
impl<'a> ReflectSerializer<'a> {
impl<'a> ReflectSerializer<'a, ()> {
/// Creates a serializer with no processor.
///
/// If you want to add custom logic for serializing certain values, use
/// [`with_processor`].
///
/// [`with_processor`]: Self::with_processor
pub fn new(value: &'a dyn PartialReflect, registry: &'a TypeRegistry) -> Self {
Self { value, registry }
Self {
value,
registry,
processor: None,
}
}
}
impl<'a> Serialize for ReflectSerializer<'a> {
impl<'a, P: ReflectSerializerProcessor> ReflectSerializer<'a, P> {
/// Creates a serializer with a processor.
///
/// If you do not need any custom logic for handling certain values, use
/// [`new`].
///
/// [`new`]: Self::new
pub fn with_processor(
value: &'a dyn PartialReflect,
registry: &'a TypeRegistry,
processor: &'a P,
) -> Self {
Self {
value,
registry,
processor: Some(processor),
}
}
}
impl<P: ReflectSerializerProcessor> Serialize for ReflectSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
S: Serializer,
{
let mut state = serializer.serialize_map(Some(1))?;
state.serialize_entry(
@ -81,7 +119,7 @@ impl<'a> Serialize for ReflectSerializer<'a> {
}
})?
.type_path(),
&TypedReflectSerializer::new(self.value, self.registry),
&TypedReflectSerializer::new_internal(self.value, self.registry, self.processor),
)?;
state.end()
}
@ -101,6 +139,10 @@ impl<'a> Serialize for ReflectSerializer<'a> {
///
/// Instead, it will output just the serialized data.
///
/// If you want to override serialization for specific values, you can pass in
/// a reference to a [`ReflectSerializerProcessor`] which will take priority
/// over all other serialization methods - see [`with_processor`].
///
/// # Example
///
/// ```
@ -125,29 +167,72 @@ impl<'a> Serialize for ReflectSerializer<'a> {
///
/// [`TypedReflectDeserializer`]: crate::serde::TypedReflectDeserializer
/// [type path]: crate::TypePath::type_path
pub struct TypedReflectSerializer<'a> {
/// [`with_processor`]: Self::with_processor
pub struct TypedReflectSerializer<'a, P = ()> {
value: &'a dyn PartialReflect,
registry: &'a TypeRegistry,
processor: Option<&'a P>,
}
impl<'a> TypedReflectSerializer<'a> {
impl<'a> TypedReflectSerializer<'a, ()> {
/// Creates a serializer with no processor.
///
/// If you want to add custom logic for serializing certain values, use
/// [`with_processor`].
///
/// [`with_processor`]: Self::with_processor
pub fn new(value: &'a dyn PartialReflect, registry: &'a TypeRegistry) -> Self {
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.set(crate::type_info_stack::TypeInfoStack::new());
Self { value, registry }
Self {
value,
registry,
processor: None,
}
}
}
impl<'a, P> TypedReflectSerializer<'a, P> {
/// Creates a serializer with a processor.
///
/// If you do not need any custom logic for handling certain values, use
/// [`new`].
///
/// [`new`]: Self::new
pub fn with_processor(
value: &'a dyn PartialReflect,
registry: &'a TypeRegistry,
processor: &'a P,
) -> Self {
#[cfg(feature = "debug_stack")]
TYPE_INFO_STACK.set(crate::type_info_stack::TypeInfoStack::new());
Self {
value,
registry,
processor: Some(processor),
}
}
/// An internal constructor for creating a serializer without resetting the type info stack.
pub(super) fn new_internal(value: &'a dyn PartialReflect, registry: &'a TypeRegistry) -> Self {
Self { value, registry }
pub(super) fn new_internal(
value: &'a dyn PartialReflect,
registry: &'a TypeRegistry,
processor: Option<&'a P>,
) -> Self {
Self {
value,
registry,
processor,
}
}
}
impl<'a> Serialize for TypedReflectSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for TypedReflectSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
S: Serializer,
{
#[cfg(feature = "debug_stack")]
{
@ -155,6 +240,23 @@ impl<'a> Serialize for TypedReflectSerializer<'a> {
TYPE_INFO_STACK.with_borrow_mut(|stack| stack.push(info));
}
}
// First, check if our processor wants to serialize this type
// This takes priority over any other serialization operations
let serializer = if let Some(processor) = self.processor {
match processor.try_serialize(self.value, self.registry, serializer) {
Ok(Ok(value)) => {
return Ok(value);
}
Err(err) => {
return Err(make_custom_error(err));
}
Ok(Err(serializer)) => serializer,
}
} else {
serializer
};
// Handle both Value case and types that have a custom `Serialize`
let (serializer, error) = match try_custom_serialize(self.value, self.registry, serializer)
{
@ -163,30 +265,54 @@ impl<'a> Serialize for TypedReflectSerializer<'a> {
};
let output = match self.value.reflect_ref() {
ReflectRef::Struct(value) => {
StructSerializer::new(value, self.registry).serialize(serializer)
ReflectRef::Struct(struct_value) => StructSerializer {
struct_value,
registry: self.registry,
processor: self.processor,
}
ReflectRef::TupleStruct(value) => {
TupleStructSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::TupleStruct(tuple_struct) => TupleStructSerializer {
tuple_struct,
registry: self.registry,
processor: self.processor,
}
ReflectRef::Tuple(value) => {
TupleSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::Tuple(tuple) => TupleSerializer {
tuple,
registry: self.registry,
processor: self.processor,
}
ReflectRef::List(value) => {
ListSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::List(list) => ListSerializer {
list,
registry: self.registry,
processor: self.processor,
}
ReflectRef::Array(value) => {
ArraySerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::Array(array) => ArraySerializer {
array,
registry: self.registry,
processor: self.processor,
}
ReflectRef::Map(value) => {
MapSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::Map(map) => MapSerializer {
map,
registry: self.registry,
processor: self.processor,
}
ReflectRef::Set(value) => {
SetSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::Set(set) => SetSerializer {
set,
registry: self.registry,
processor: self.processor,
}
ReflectRef::Enum(value) => {
EnumSerializer::new(value, self.registry).serialize(serializer)
.serialize(serializer),
ReflectRef::Enum(enum_value) => EnumSerializer {
enum_value,
registry: self.registry,
processor: self.processor,
}
.serialize(serializer),
#[cfg(feature = "functions")]
ReflectRef::Function(_) => Err(make_custom_error("functions cannot be serialized")),
ReflectRef::Opaque(_) => Err(error),

View file

@ -1,26 +1,27 @@
use crate::{serde::TypedReflectSerializer, Set, TypeRegistry};
use serde::{ser::SerializeSeq, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`Set`] values.
pub(super) struct SetSerializer<'a> {
set: &'a dyn Set,
registry: &'a TypeRegistry,
pub(super) struct SetSerializer<'a, P> {
pub set: &'a dyn Set,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> SetSerializer<'a> {
pub fn new(set: &'a dyn Set, registry: &'a TypeRegistry) -> Self {
Self { set, registry }
}
}
impl<'a> Serialize for SetSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for SetSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_seq(Some(self.set.len()))?;
for value in self.set.iter() {
state.serialize_element(&TypedReflectSerializer::new_internal(value, self.registry))?;
state.serialize_element(&TypedReflectSerializer::new_internal(
value,
self.registry,
self.processor,
))?;
}
state.end()
}

View file

@ -4,22 +4,16 @@ use crate::{
};
use serde::{ser::SerializeStruct, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`Struct`] values.
pub(super) struct StructSerializer<'a> {
struct_value: &'a dyn Struct,
registry: &'a TypeRegistry,
pub(super) struct StructSerializer<'a, P> {
pub struct_value: &'a dyn Struct,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> StructSerializer<'a> {
pub fn new(struct_value: &'a dyn Struct, registry: &'a TypeRegistry) -> Self {
Self {
struct_value,
registry,
}
}
}
impl<'a> Serialize for StructSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for StructSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -63,7 +57,7 @@ impl<'a> Serialize for StructSerializer<'a> {
let key = struct_info.field_at(index).unwrap().name();
state.serialize_field(
key,
&TypedReflectSerializer::new_internal(value, self.registry),
&TypedReflectSerializer::new_internal(value, self.registry, self.processor),
)?;
}
state.end()

View file

@ -4,22 +4,16 @@ use crate::{
};
use serde::{ser::SerializeTupleStruct, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`TupleStruct`] values.
pub(super) struct TupleStructSerializer<'a> {
tuple_struct: &'a dyn TupleStruct,
registry: &'a TypeRegistry,
pub(super) struct TupleStructSerializer<'a, P> {
pub tuple_struct: &'a dyn TupleStruct,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> TupleStructSerializer<'a> {
pub fn new(tuple_struct: &'a dyn TupleStruct, registry: &'a TypeRegistry) -> Self {
Self {
tuple_struct,
registry,
}
}
}
impl<'a> Serialize for TupleStructSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for TupleStructSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -53,7 +47,7 @@ impl<'a> Serialize for TupleStructSerializer<'a> {
let field = self.tuple_struct.field(0).unwrap();
return serializer.serialize_newtype_struct(
tuple_struct_info.type_path_table().ident().unwrap(),
&TypedReflectSerializer::new_internal(field, self.registry),
&TypedReflectSerializer::new_internal(field, self.registry, self.processor),
);
}
@ -69,7 +63,11 @@ impl<'a> Serialize for TupleStructSerializer<'a> {
{
continue;
}
state.serialize_field(&TypedReflectSerializer::new_internal(value, self.registry))?;
state.serialize_field(&TypedReflectSerializer::new_internal(
value,
self.registry,
self.processor,
))?;
}
state.end()
}

View file

@ -1,19 +1,16 @@
use crate::{serde::TypedReflectSerializer, Tuple, TypeRegistry};
use serde::{ser::SerializeTuple, Serialize};
use super::ReflectSerializerProcessor;
/// A serializer for [`Tuple`] values.
pub(super) struct TupleSerializer<'a> {
tuple: &'a dyn Tuple,
registry: &'a TypeRegistry,
pub(super) struct TupleSerializer<'a, P> {
pub tuple: &'a dyn Tuple,
pub registry: &'a TypeRegistry,
pub processor: Option<&'a P>,
}
impl<'a> TupleSerializer<'a> {
pub fn new(tuple: &'a dyn Tuple, registry: &'a TypeRegistry) -> Self {
Self { tuple, registry }
}
}
impl<'a> Serialize for TupleSerializer<'a> {
impl<P: ReflectSerializerProcessor> Serialize for TupleSerializer<'_, P> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
@ -21,7 +18,11 @@ impl<'a> Serialize for TupleSerializer<'a> {
let mut state = serializer.serialize_tuple(self.tuple.field_len())?;
for value in self.tuple.iter_fields() {
state.serialize_element(&TypedReflectSerializer::new_internal(value, self.registry))?;
state.serialize_element(&TypedReflectSerializer::new_internal(
value,
self.registry,
self.processor,
))?;
}
state.end()
}