mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 12:13:25 +00:00
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:
parent
ded5ce27ae
commit
17c4b070ab
12 changed files with 628 additions and 138 deletions
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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, ®istry, &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, ®istry, &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, ®istry, &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::*;
|
||||
|
|
196
crates/bevy_reflect/src/serde/ser/processor.rs
Normal file
196
crates/bevy_reflect/src/serde/ser/processor.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
|
||||
/// 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 }
|
||||
Self {
|
||||
value,
|
||||
registry,
|
||||
processor: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Serialize for TypedReflectSerializer<'a> {
|
||||
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,
|
||||
processor: Option<&'a P>,
|
||||
) -> Self {
|
||||
Self {
|
||||
value,
|
||||
registry,
|
||||
processor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue