diff --git a/crates/bevy_reflect/src/serde/de/processor.rs b/crates/bevy_reflect/src/serde/de/processor.rs index 7dbe0388b4..1d4d81f4b9 100644 --- a/crates/bevy_reflect/src/serde/de/processor.rs +++ b/crates/bevy_reflect/src/serde/de/processor.rs @@ -15,6 +15,8 @@ use crate::{PartialReflect, TypeRegistration, TypeRegistry}; /// deserializer and give back a [`Box`], 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. diff --git a/crates/bevy_reflect/src/serde/ser/arrays.rs b/crates/bevy_reflect/src/serde/ser/arrays.rs index 16c741a680..9d565344ae 100644 --- a/crates/bevy_reflect/src/serde/ser/arrays.rs +++ b/crates/bevy_reflect/src/serde/ser/arrays.rs @@ -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 Serialize for ArraySerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() } diff --git a/crates/bevy_reflect/src/serde/ser/enums.rs b/crates/bevy_reflect/src/serde/ser/enums.rs index 4b71207a97..86c4360ce0 100644 --- a/crates/bevy_reflect/src/serde/ser/enums.rs +++ b/crates/bevy_reflect/src/serde/ser/enums.rs @@ -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 Serialize for EnumSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() diff --git a/crates/bevy_reflect/src/serde/ser/lists.rs b/crates/bevy_reflect/src/serde/ser/lists.rs index b52d52202f..4c3fb6b33d 100644 --- a/crates/bevy_reflect/src/serde/ser/lists.rs +++ b/crates/bevy_reflect/src/serde/ser/lists.rs @@ -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 Serialize for ListSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() } diff --git a/crates/bevy_reflect/src/serde/ser/maps.rs b/crates/bevy_reflect/src/serde/ser/maps.rs index 354169193b..c1a8c287c1 100644 --- a/crates/bevy_reflect/src/serde/ser/maps.rs +++ b/crates/bevy_reflect/src/serde/ser/maps.rs @@ -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 Serialize for MapSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() diff --git a/crates/bevy_reflect/src/serde/ser/mod.rs b/crates/bevy_reflect/src/serde/ser/mod.rs index ead36d4819..ba12b7fb4f 100644 --- a/crates/bevy_reflect/src/serde/ser/mod.rs +++ b/crates/bevy_reflect/src/serde/ser/mod.rs @@ -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( + &self, + value: &dyn PartialReflect, + _: &TypeRegistry, + serializer: S, + ) -> Result, 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::() { + Ok(Ok(serializer.serialize_str("custom!")?)) + } else { + Ok(Err(serializer)) + } + } + } + + let value = Foo { bar: 123, qux: 456 }; + + let mut registry = TypeRegistry::new(); + registry.register::(); + + 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( + &self, + value: &dyn PartialReflect, + _: &TypeRegistry, + serializer: S, + ) -> Result, 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::() { + Ok(Ok(serializer.serialize_str("an i32")?)) + } else if type_id == TypeId::of::() { + 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::(); + registry.register::(); + + 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( + &self, + value: &dyn PartialReflect, + _: &TypeRegistry, + serializer: S, + ) -> Result, 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::() { + 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::*; diff --git a/crates/bevy_reflect/src/serde/ser/processor.rs b/crates/bevy_reflect/src/serde/ser/processor.rs new file mode 100644 index 0000000000..cf31ab7566 --- /dev/null +++ b/crates/bevy_reflect/src/serde/ser/processor.rs @@ -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); +/// # #[derive(Debug, Clone, Reflect)] +/// # struct Mesh; +/// # +/// # struct ReflectHandle; +/// # impl TypeData for ReflectHandle { +/// # fn clone_type_data(&self) -> Box { +/// # unimplemented!() +/// # } +/// # } +/// # impl ReflectHandle { +/// # fn downcast_handle_untyped(&self, handle: &(dyn Any + 'static)) -> Option { +/// # unimplemented!() +/// # } +/// # } +/// # +/// # #[derive(Debug, Clone)] +/// # struct UntypedHandle; +/// # impl UntypedHandle { +/// # fn path(&self) -> Option<&str> { +/// # unimplemented!() +/// # } +/// # } +/// # type AssetError = Box; +/// # +/// #[derive(Debug, Clone, Reflect)] +/// struct MyAsset { +/// name: String, +/// mesh: Handle, +/// } +/// +/// struct HandleProcessor; +/// +/// impl ReflectSerializerProcessor for HandleProcessor { +/// fn try_serialize( +/// &self, +/// value: &dyn PartialReflect, +/// registry: &TypeRegistry, +/// serializer: S, +/// ) -> Result, 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::(type_id) else { +/// // this isn't a `Handle` +/// 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, 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( + /// &self, + /// value: &dyn PartialReflect, + /// registry: &TypeRegistry, + /// serializer: S, + /// ) -> Result, S::Error> + /// where + /// S: serde::Serializer + /// { + /// if let Some(value) = value.try_downcast_ref::() { + /// 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( + &self, + value: &dyn PartialReflect, + registry: &TypeRegistry, + serializer: S, + ) -> Result, S::Error> + where + S: Serializer; +} + +impl ReflectSerializerProcessor for () { + fn try_serialize( + &self, + _value: &dyn PartialReflect, + _registry: &TypeRegistry, + serializer: S, + ) -> Result, S::Error> + where + S: Serializer, + { + Ok(Err(serializer)) + } +} diff --git a/crates/bevy_reflect/src/serde/ser/serializer.rs b/crates/bevy_reflect/src/serde/ser/serializer.rs index a803399829..afe6b56b1d 100644 --- a/crates/bevy_reflect/src/serde/ser/serializer.rs +++ b/crates/bevy_reflect/src/serde/ser/serializer.rs @@ -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 Serialize for ReflectSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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 Serialize for TypedReflectSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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), diff --git a/crates/bevy_reflect/src/serde/ser/sets.rs b/crates/bevy_reflect/src/serde/ser/sets.rs index 343c253eb2..34e8899d58 100644 --- a/crates/bevy_reflect/src/serde/ser/sets.rs +++ b/crates/bevy_reflect/src/serde/ser/sets.rs @@ -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 Serialize for SetSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() } diff --git a/crates/bevy_reflect/src/serde/ser/structs.rs b/crates/bevy_reflect/src/serde/ser/structs.rs index c8c2b87b44..4eb3e76700 100644 --- a/crates/bevy_reflect/src/serde/ser/structs.rs +++ b/crates/bevy_reflect/src/serde/ser/structs.rs @@ -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 Serialize for StructSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() diff --git a/crates/bevy_reflect/src/serde/ser/tuple_structs.rs b/crates/bevy_reflect/src/serde/ser/tuple_structs.rs index 55e171feae..5bf2ec64ae 100644 --- a/crates/bevy_reflect/src/serde/ser/tuple_structs.rs +++ b/crates/bevy_reflect/src/serde/ser/tuple_structs.rs @@ -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 Serialize for TupleStructSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() } diff --git a/crates/bevy_reflect/src/serde/ser/tuples.rs b/crates/bevy_reflect/src/serde/ser/tuples.rs index 133818dfa9..195d1e492d 100644 --- a/crates/bevy_reflect/src/serde/ser/tuples.rs +++ b/crates/bevy_reflect/src/serde/ser/tuples.rs @@ -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 Serialize for TupleSerializer<'_, P> { fn serialize(&self, serializer: S) -> Result 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() }