From 78a2e7aea6c28a20296afed845a487c39252f0ae Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 11 Feb 2022 01:47:31 +0100 Subject: [PATCH] Add basic struct flattening support (#16) This adds basic support for the `flatten` attribute when deriving structs. --- CHANGELOG.md | 1 + deser-derive/src/attr.rs | 22 +++++ deser-derive/src/de.rs | 121 ++++++++++++++++++----- deser-derive/src/ser.rs | 133 +++++++++++++++++++------ deser-path/src/ser.rs | 36 ++++--- deser/src/de/mod.rs | 45 +++++++-- deser/src/de/owned.rs | 99 +++++++++++++++++++ deser/src/derive.rs | 3 + deser/src/ser/impls.rs | 44 +++++---- deser/src/ser/mod.rs | 30 +++--- deser/tests/test_custom_map.rs | 10 +- deser/tests/test_de_derive.rs | 85 ++++++++++++++++ deser/tests/test_ser_derive.rs | 154 +++++++++++++++++++++++++++++ examples/derive/src/main.rs | 13 +++ examples/manual-struct/src/main.rs | 9 +- 15 files changed, 692 insertions(+), 113 deletions(-) create mode 100644 deser/src/de/owned.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a9f129..1db7b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to deser are documented here. directly on the `Sink`. - The serializer state and deserializer state is now passed to `next_key`/ `next_value` and `next` on the sinks and emitters. +- Added support for `#[deser(flatten)]`. ## 0.5.0 diff --git a/deser-derive/src/attr.rs b/deser-derive/src/attr.rs index 89c7d22..e74f1fd 100644 --- a/deser-derive/src/attr.rs +++ b/deser-derive/src/attr.rs @@ -270,6 +270,7 @@ pub struct FieldAttrs<'a> { rename: Option, aliases: Vec, default: Option, + flatten: bool, skip_serializing_if: Option, } @@ -280,6 +281,7 @@ impl<'a> FieldAttrs<'a> { rename: None, aliases: Vec::new(), default: None, + flatten: false, skip_serializing_if: None, }; @@ -318,6 +320,15 @@ impl<'a> FieldAttrs<'a> { "default", &nv.lit, )?)); } + syn::Meta::Path(path) if path.is_ident("flatten") => { + if rv.flatten { + return Err(syn::Error::new_spanned( + meta, + "duplicate flatten attribute", + )); + } + rv.flatten = true; + } syn::Meta::NameValue(nv) if nv.path.is_ident("skip_serializing_if") => { if rv.skip_serializing_if.is_some() { return Err(syn::Error::new_spanned( @@ -335,6 +346,13 @@ impl<'a> FieldAttrs<'a> { } } + if rv.flatten && rv.default.is_some() { + return Err(syn::Error::new_spanned( + field, + "cannot combine flatten and default", + )); + } + Ok(rv) } @@ -357,6 +375,10 @@ impl<'a> FieldAttrs<'a> { self.default.as_ref() } + pub fn flatten(&self) -> bool { + self.flatten + } + pub fn skip_serializing_if(&self) -> Option<&syn::ExprPath> { self.skip_serializing_if.as_ref() } diff --git a/deser-derive/src/de.rs b/deser-derive/src/de.rs index d1ba2ef..dd223c0 100644 --- a/deser-derive/src/de.rs +++ b/deser-derive/src/de.rs @@ -27,49 +27,79 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re let container_attrs = ContainerAttrs::of(input)?; let type_name = container_attrs.container_name(); - let fieldname = &fields.named.iter().map(|f| &f.ident).collect::>(); - let fieldty = fields.named.iter().map(|f| &f.ty); - let sink_fieldname = &fields + let attrs = fields .named .iter() - .map(|f| { + .map(FieldAttrs::of) + .collect::>>()?; + let fieldname = attrs.iter().map(|x| &x.field().ident).collect::>(); + let sink_fieldname = attrs + .iter() + .map(|x| { syn::Ident::new( - &format!("field_{}", f.ident.as_ref().unwrap()), + &format!("field_{}", x.field().ident.as_ref().unwrap()), Span::call_site(), ) }) .collect::>(); - let attrs = fields - .named + let sink_fieldty = attrs .iter() - .map(FieldAttrs::of) - .collect::>>()?; + .map(|f| { + let ty = &f.field().ty; + if f.flatten() { + quote! { + ::deser::de::OwnedSink<#ty> + } + } else { + quote! { + ::deser::__derive::Option<#ty> + } + } + }) + .collect::>(); + let sink_defaults = attrs + .iter() + .map(|f| { + if f.flatten() { + quote! { + ::deser::de::OwnedSink::deserialize() + } + } else { + quote! { + ::deser::__derive::None + } + } + }) + .collect::>(); let mut seen_names = HashSet::new(); let mut first_duplicate_name = None; let matcher = attrs .iter() - .map(|x| { + .zip(sink_fieldname.iter()) + .filter_map(|(x, fieldname)| { + if x.flatten() { + return None; + } + let name = x.name(&container_attrs).to_string(); if first_duplicate_name.is_none() && seen_names.contains(&name) { first_duplicate_name = Some((name.clone(), x.field())); } seen_names.insert(name.clone()); - let mut rv = quote! { - ::deser::__derive::Some(#name) - }; + let mut rv = quote! { #name }; for alias in x.aliases() { let alias = alias.clone(); if first_duplicate_name.is_none() && seen_names.contains(&alias) { first_duplicate_name = Some((alias.clone(), x.field())); } seen_names.insert(alias.clone()); - rv = quote! { - #rv | ::deser::__derive::Some(#alias) - }; + rv = quote! { #rv | #alias }; } - rv + Some(quote! { + #rv => return ::deser::__derive::Ok(::deser::__derive::Some(::deser::Deserialize::deserialize_into(&mut self.#fieldname))), + }) }) .collect::>(); @@ -103,6 +133,17 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re .map(|(name, attrs)| { if attrs.default().is_some() { quote! { #name } + } else if attrs.flatten() { + // this should never happen unless the inner deserializer fucked up + let error = format!( + "Failed to deserialize flattened field '{}'", + attrs.name(&container_attrs) + ); + quote! { + #name.ok_or_else(|| { + ::deser::Error::new(::deser::ErrorKind::Unexpected, #error) + })? + } } else if container_attrs.default().is_some() { quote! { #name.unwrap() } } else { @@ -115,6 +156,19 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re } }) .collect::>(); + let flatten_fields = sink_fieldname + .iter() + .zip(attrs.iter()) + .filter_map( + |(name, attrs)| { + if attrs.flatten() { + Some(name) + } else { + None + } + }, + ) + .collect::>(); let stage2_default = if container_attrs.default().is_some() { let need_container_default = sink_fieldname @@ -164,10 +218,11 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re slot: &'__a mut ::deser::__derive::Option<#ident #ty_generics>, key: ::deser::__derive::Option, #( - #sink_fieldname: ::deser::__derive::Option<#fieldty>, + #sink_fieldname: #sink_fieldty, )* } + #[automatically_derived] impl #impl_generics ::deser::Deserialize for #ident #ty_generics #bounded_where_clause { fn deserialize_into( __slot: &mut ::deser::__derive::Option, @@ -176,12 +231,13 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re slot: __slot, key: ::deser::__derive::None, #( - #sink_fieldname: ::deser::__derive::None, + #sink_fieldname: #sink_defaults, )* }) } } + #[automatically_derived] impl #wrapper_impl_generics ::deser::de::Sink for __Sink #wrapper_ty_generics #bounded_where_clause { fn descriptor(&self) -> &dyn ::deser::Descriptor { &__Descriptor @@ -202,16 +258,36 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re fn next_value(&mut self, __state: &::deser::de::DeserializerState) -> ::deser::__derive::Result<::deser::de::SinkHandle> { - match self.key.take().as_deref() { + let __key = self.key.take().unwrap(); + ::deser::__derive::Ok(match self.value_for_key(&__key, __state)? { + ::deser::__derive::Some(__sink) => __sink, + ::deser::__derive::None => ::deser::de::SinkHandle::null(), + }) + } + + fn value_for_key(&mut self, __key: &str, __state: &::deser::de::DeserializerState) + -> ::deser::__derive::Result<::deser::__derive::Option<::deser::de::SinkHandle>> + { + match __key { #( - #matcher => ::deser::__derive::Ok(::deser::Deserialize::deserialize_into(&mut self.#sink_fieldname)), + #matcher )* - _ => ::deser::__derive::Ok(::deser::de::SinkHandle::null()), + __other => { + #( + if let ::deser::__derive::Some(__sink) = self.#flatten_fields.borrow_mut().value_for_key(__other, __state)? { + return ::deser::__derive::Ok(::deser::__derive::Some(__sink)); + } + )* + } } + ::deser::__derive::Ok(::deser::__derive::None) } fn finish(&mut self, __state: &::deser::de::DeserializerState) -> ::deser::__derive::Result<()> { #![allow(unused_mut)] + #( + self.#flatten_fields.borrow_mut().finish(__state)?; + )* #( let mut #sink_fieldname = self.#sink_fieldname.#field_stage1_default; )* @@ -316,6 +392,7 @@ pub fn derive_enum( slot: ::deser::__derive::Option<#ident>, } + #[automatically_derived] impl ::deser::de::Deserialize for #ident { fn deserialize_into( __slot: &mut ::deser::__derive::Option diff --git a/deser-derive/src/ser.rs b/deser-derive/src/ser.rs index 9edb068..e9a9b9e 100644 --- a/deser-derive/src/ser.rs +++ b/deser-derive/src/ser.rs @@ -25,47 +25,120 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re let container_attrs = ContainerAttrs::of(input)?; let type_name = container_attrs.container_name(); - let fieldname = &fields.named.iter().map(|f| &f.ident).collect::>(); let attrs = fields .named .iter() .map(FieldAttrs::of) .collect::>>()?; - let fieldstr = attrs - .iter() - .map(|x| x.name(&container_attrs)) - .collect::>(); - let skip_if = attrs + + let temp_emitter = if attrs.iter().any(|x| x.flatten()) { + Some(quote! { + nested_emitter: ::deser::__derive::Option<::deser::__derive::Box>, + nested_emitter_exhausted: bool, + }) + } else { + None + }; + let temp_emitter_init = if attrs.iter().any(|x| x.flatten()) { + Some(quote! { + nested_emitter: ::deser::__derive::None, + nested_emitter_exhausted: true, + }) + } else { + None + }; + let state_handler = attrs .iter() - .zip(fieldname.iter()) - .map(|(attrs, name)| { - let field_skip = if let Some(path) = attrs.skip_serializing_if() { + .enumerate() + .map(|(index, attrs)| { + let name = &attrs.field().ident; + let optional_skip = if container_attrs.skip_serializing_optionals() { quote! { - if #path(&self.data.#name) { + if __handle.is_optional() { continue; } } } else { quote! {} }; - let optional_skip = if container_attrs.skip_serializing_optionals() { + if !attrs.flatten() { + let fieldstr = attrs.name(&container_attrs); + let field_skip = if let Some(path) = attrs.skip_serializing_if() { + quote! { + if #path(&self.data.#name) { + continue; + } + } + } else { + quote! {} + }; quote! { - if __handle.is_optional() { - continue; + #index => { + self.index = __index + 1; + #field_skip + let __handle = ::deser::ser::SerializeHandle::to(&self.data.#name); + #optional_skip + return ::deser::__derive::Ok(::deser::__derive::Some(( + ::deser::__derive::Cow::Borrowed(#fieldstr), + __handle, + ))); } } } else { - quote! {} - }; - quote! { - #field_skip - #optional_skip + let field_skip = if let Some(path) = attrs.skip_serializing_if() { + quote! { + if #path(&self.data.#name) { + self.index += 1; + continue; + } + } + } else { + quote! {} + }; + quote! { + #index => { + #field_skip + if self.nested_emitter_exhausted { + self.nested_emitter = match self.data.#name.serialize(__state)? { + ::deser::ser::Chunk::Struct(__inner) => { + Some(__inner) + } + _ => return ::deser::__derive::Err(::deser::Error::new( + ::deser::ErrorKind::Unexpected, + "unable to flatten on struct into struct" + )) + }; + self.nested_emitter_exhausted = false; + } + match self.nested_emitter.as_mut().unwrap().next(__state)? { + ::deser::__derive::None => { + self.index += 1; + self.nested_emitter_exhausted = true; + self.data.#name.finish(__state)?; + continue; + } + // we need this transmute here because of limitations in the borrow + // checker. The borrow checker does not understand that the borrow + // does not continue into the next loop iteration. If polonius ever + // makes it into Rust this can go. + // + // This can be validated with `-Zpolonius` + ::deser::__derive::Some((__key, __handle)) => { + #optional_skip + return ::deser::__derive::Ok(::deser::__derive::Some(unsafe { + ::std::mem::transmute::<_, _>(( + __key, + __handle + )) + })) + } + } + } + } } }) .collect::>(); - let index = 0usize..; - let wrapper_generics = with_lifetime_bound(&input.generics, "'__a"); let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl(); let bound = syn::parse_quote!(::deser::Serialize); @@ -74,6 +147,7 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re Ok(quote! { #[allow(non_upper_case_globals)] const #dummy: () = { + #[automatically_derived] impl #impl_generics ::deser::Serialize for #ident #ty_generics #bounded_where_clause { fn descriptor(&self) -> &dyn ::deser::Descriptor { &__Descriptor @@ -82,6 +156,7 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re ::deser::__derive::Ok(::deser::ser::Chunk::Struct(Box::new(__StructEmitter { data: self, index: 0, + #temp_emitter_init }))) } } @@ -89,6 +164,7 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re struct __StructEmitter #wrapper_impl_generics #where_clause { data: &'__a #ident #ty_generics, index: usize, + #temp_emitter } struct __Descriptor; @@ -99,25 +175,19 @@ fn derive_struct(input: &syn::DeriveInput, fields: &syn::FieldsNamed) -> syn::Re } } + #[automatically_derived] impl #wrapper_impl_generics ::deser::ser::StructEmitter for __StructEmitter #wrapper_ty_generics #bounded_where_clause { fn next(&mut self, __state: &::deser::ser::SerializerState) - -> ::deser::__derive::Option<(deser::__derive::StrCow, ::deser::ser::SerializeHandle)> + -> ::deser::__derive::Result<::deser::__derive::Option<(deser::__derive::StrCow, ::deser::ser::SerializeHandle)>> { + #[allow(clippy::never_loop)] loop { let __index = self.index; - self.index = __index + 1; match __index { #( - #index => { - let __handle = ::deser::ser::SerializeHandle::to(&self.data.#fieldname); - #skip_if - return::deser::__derive::Some(( - ::deser::__derive::Cow::Borrowed(#fieldstr), - __handle, - )) - } + #state_handler )* - _ => return ::deser::__derive::None, + _ => return ::deser::__derive::Ok(::deser::__derive::None), } } } @@ -165,6 +235,7 @@ fn derive_enum(input: &syn::DeriveInput, enumeration: &syn::DataEnum) -> syn::Re Ok(quote! { #[allow(non_upper_case_globals)] const #dummy: () = { + #[automatically_derived] impl ::deser::Serialize for #ident { fn serialize(&self, __state: &::deser::ser::SerializerState) -> ::deser::__derive::Result<::deser::ser::Chunk> diff --git a/deser-path/src/ser.rs b/deser-path/src/ser.rs index 18b849f..708ce8b 100644 --- a/deser-path/src/ser.rs +++ b/deser-path/src/ser.rs @@ -49,14 +49,20 @@ struct PathStructEmitter<'a> { } impl<'a> StructEmitter for PathStructEmitter<'a> { - fn next(&mut self, state: &SerializerState) -> Option<(Cow<'_, str>, SerializeHandle)> { - let (key, value) = self.emitter.next(state)?; + fn next( + &mut self, + state: &SerializerState, + ) -> Result, SerializeHandle)>, Error> { + let (key, value) = match self.emitter.next(state)? { + Some(result) => result, + None => return Ok(None), + }; let new_segment = PathSegment::Key(key.to_string()); let value_serializable = SegmentPushingSerializable { serializable: value, segment: RefCell::new(Some(new_segment)), }; - Some((key, SerializeHandle::boxed(value_serializable))) + Ok(Some((key, SerializeHandle::boxed(value_serializable)))) } } @@ -66,25 +72,28 @@ struct PathMapEmitter<'a> { } impl<'a> MapEmitter for PathMapEmitter<'a> { - fn next_key(&mut self, state: &SerializerState) -> Option { + fn next_key(&mut self, state: &SerializerState) -> Result, Error> { let key_serializable = SegmentCollectingSerializable { - serializable: self.emitter.next_key(state)?, + serializable: match self.emitter.next_key(state)? { + Some(result) => result, + None => return Ok(None), + }, segment: self.path_segment.clone(), }; - Some(SerializeHandle::boxed(key_serializable)) + Ok(Some(SerializeHandle::boxed(key_serializable))) } - fn next_value(&mut self, state: &SerializerState) -> SerializeHandle { + fn next_value(&mut self, state: &SerializerState) -> Result { let new_segment = self .path_segment .borrow_mut() .take() .unwrap_or(PathSegment::Unknown); let value_serializable = SegmentPushingSerializable { - serializable: self.emitter.next_value(state), + serializable: self.emitter.next_value(state)?, segment: RefCell::new(Some(new_segment)), }; - SerializeHandle::boxed(value_serializable) + Ok(SerializeHandle::boxed(value_serializable)) } } @@ -94,16 +103,19 @@ struct PathSeqEmitter<'a> { } impl<'a> SeqEmitter for PathSeqEmitter<'a> { - fn next(&mut self, state: &SerializerState) -> Option { + fn next(&mut self, state: &SerializerState) -> Result, Error> { let index = self.index; self.index += 1; - let value = self.emitter.next(state)?; + let value = match self.emitter.next(state)? { + Some(result) => result, + None => return Ok(None), + }; let new_segment = PathSegment::Index(index); let item_serializable = SegmentPushingSerializable { serializable: value, segment: RefCell::new(Some(new_segment)), }; - Some(SerializeHandle::boxed(item_serializable)) + Ok(Some(SerializeHandle::boxed(item_serializable))) } } diff --git a/deser/src/de/mod.rs b/deser/src/de/mod.rs index 4c67da3..cf01922 100644 --- a/deser/src/de/mod.rs +++ b/deser/src/de/mod.rs @@ -121,15 +121,23 @@ //! Ok(Deserialize::deserialize_into(&mut self.key)) //! } //! -//! fn next_value(&mut self, _state: &DeserializerState) -> Result { -//! // whenever we are looking for a value slot, look at the last key -//! // to decide which value slot to connect. -//! match self.key.take().as_deref() { -//! Some("enabled") => Ok(Deserialize::deserialize_into(&mut self.enabled_field)), -//! Some("name") => Ok(Deserialize::deserialize_into(&mut self.name_field)), -//! // if we don't know the key, return a null handle to drop the value. -//! _ => Ok(SinkHandle::null()), -//! } +//! fn next_value(&mut self, state: &DeserializerState) -> Result { +//! let key = self.key.take().unwrap(); +//! // since we implement a sink for a struct, move the actual logic for +//! // matching into `value_for_key` so that our deserializer can support +//! // struct flattening. If we don't know the key, just return a null +//! // handle to ignore it. +//! Ok(self.value_for_key(&key, state)?.unwrap_or_else(SinkHandle::null)) +//! } +//! +//! fn value_for_key(&mut self, key: &str, _state: &DeserializerState) +//! -> Result, Error> +//! { +//! Ok(Some(match key { +//! "enabled" => Deserialize::deserialize_into(&mut self.enabled_field), +//! "name" => Deserialize::deserialize_into(&mut self.name_field), +//! _ => return Ok(None) +//! })) //! } //! //! fn finish(&mut self, _state: &DeserializerState) -> Result<(), Error> { @@ -190,6 +198,9 @@ use crate::extensions::Extensions; mod ignore; mod impls; +mod owned; + +pub use self::owned::OwnedSink; __make_slot_wrapper!((pub), SlotWrapper); @@ -348,6 +359,22 @@ pub trait Sink { Ok(SinkHandle::null()) } + /// Returns a value sink for a specific struct field. + /// + /// This is a special method that is supposed to be implemented by structs + /// if they want to support flattening. A struct that gets flattened into + /// another struct will have this method called to figure out if a key is + /// used by it. The default implementation always returns `None`. + fn value_for_key( + &mut self, + key: &str, + state: &DeserializerState, + ) -> Result, Error> { + let _ = key; + let _ = state; + Ok(None) + } + /// Called after [`atom`](Self::atom), [`map`](Self::map) or [`seq](Self::seq). /// /// The default implementation does nothing. diff --git a/deser/src/de/owned.rs b/deser/src/de/owned.rs new file mode 100644 index 0000000..7a6b47b --- /dev/null +++ b/deser/src/de/owned.rs @@ -0,0 +1,99 @@ +use std::mem::{transmute, ManuallyDrop}; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; + +use crate::de::{Deserialize, SinkHandle}; + +struct NonuniqueBox { + ptr: NonNull, +} + +impl NonuniqueBox { + pub fn new(value: T) -> Self { + NonuniqueBox::from(Box::new(value)) + } +} + +impl From> for NonuniqueBox { + fn from(boxed: Box) -> Self { + let ptr = Box::into_raw(boxed); + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + NonuniqueBox { ptr } + } +} + +impl Deref for NonuniqueBox { + type Target = T; + fn deref(&self) -> &Self::Target { + unsafe { self.ptr.as_ref() } + } +} + +impl DerefMut for NonuniqueBox { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { self.ptr.as_mut() } + } +} + +impl Drop for NonuniqueBox { + fn drop(&mut self) { + let ptr = self.ptr.as_ptr(); + let _ = unsafe { Box::from_raw(ptr) }; + } +} + +/// Utility to bundle a sink with a slot. +/// +/// There are situations where one wants to be able to deserialize into +/// a slot that needs to be allocated on the heap and hold it together +/// with the sink handle. Rust's lifetimes make this impossible so this +/// abstraction is provided to allow this. +pub struct OwnedSink { + storage: NonuniqueBox>, + sink: Option>>, +} + +impl OwnedSink { + /// Creates a new owned sink for a given type. + /// + /// This begins the deserialization with [`Deserialize::deserialize_into`] + /// into a slot contained within the owned sink. To extract the final + /// value use [`take`](Self::take). + pub fn deserialize() -> OwnedSink { + let mut storage = NonuniqueBox::new(None); + unsafe { + let ptr = transmute::<_, &mut Option>(&mut *storage); + let sink = extend_lifetime!(T::deserialize_into(ptr), SinkHandle<'_>); + OwnedSink { + storage, + sink: Some(ManuallyDrop::new(extend_lifetime!(sink, SinkHandle<'_>))), + } + } + } + + /// Immutably borrows from an owned sink. + pub fn borrow(&mut self) -> &SinkHandle<'_> { + unsafe { extend_lifetime!(self.sink.as_ref().unwrap(), &SinkHandle<'_>) } + } + + /// Mutably borrows from the owned sink. + #[allow(clippy::should_implement_trait)] + pub fn borrow_mut(&mut self) -> &mut SinkHandle<'_> { + unsafe { extend_lifetime!(self.sink.as_mut().unwrap(), &mut SinkHandle<'_>) } + } + + /// Takes the value produced by the sink. + pub fn take(&mut self) -> Option { + self.storage.take() + } +} + +impl Drop for OwnedSink { + fn drop(&mut self) { + unsafe { + if let Some(ref mut sink) = self.sink.take() { + ManuallyDrop::drop(sink); + } + } + } +} diff --git a/deser/src/derive.rs b/deser/src/derive.rs index 40ac4c8..60faa15 100644 --- a/deser/src/derive.rs +++ b/deser/src/derive.rs @@ -64,6 +64,9 @@ //! if it should be skipped during serialization. //! * `#[deser(alias = "...")]`: provides an alias for the field name for deserialization. This is ignored //! for serialization. +//! * `#[deser(flatten)]`: when added to a nested struct field causes that field to be flattened into the +//! parent struct. Note that flattening only works with structs (more specifically with string) keys. +//! This feature is enabled by [`value_for_key`](crate::de::Sink::value_for_key). //! //! ## Enum Variant Attributes //! diff --git a/deser/src/ser/impls.rs b/deser/src/ser/impls.rs index 59cc253..0ffcbff 100644 --- a/deser/src/ser/impls.rs +++ b/deser/src/ser/impls.rs @@ -174,8 +174,8 @@ where struct SliceEmitter<'a, T>(std::slice::Iter<'a, T>); impl<'a, T: Serialize> SeqEmitter for SliceEmitter<'a, T> { - fn next(&mut self, _state: &SerializerState) -> Option { - self.0.next().map(SerializeHandle::to) + fn next(&mut self, _state: &SerializerState) -> Result, Error> { + Ok(self.0.next().map(SerializeHandle::to)) } } @@ -197,15 +197,18 @@ where K: Serialize, V: Serialize, { - fn next_key(&mut self, _state: &SerializerState) -> Option { - self.0.next().map(|(k, v)| { + fn next_key( + &mut self, + _state: &SerializerState, + ) -> Result, Error> { + Ok(self.0.next().map(|(k, v)| { self.1 = Some(v); SerializeHandle::to(k) - }) + })) } - fn next_value(&mut self, _state: &SerializerState) -> SerializeHandle { - SerializeHandle::to(self.1.unwrap()) + fn next_value(&mut self, _state: &SerializerState) -> Result { + Ok(SerializeHandle::to(self.1.unwrap())) } } @@ -232,15 +235,18 @@ where K: Serialize, V: Serialize, { - fn next_key(&mut self, _state: &SerializerState) -> Option { - self.0.next().map(|(k, v)| { + fn next_key( + &mut self, + _state: &SerializerState, + ) -> Result, Error> { + Ok(self.0.next().map(|(k, v)| { self.1 = Some(v); SerializeHandle::to(k) - }) + })) } - fn next_value(&mut self, _state: &SerializerState) -> SerializeHandle { - SerializeHandle::to(self.1.unwrap()) + fn next_value(&mut self, _state: &SerializerState) -> Result { + Ok(SerializeHandle::to(self.1.unwrap())) } } @@ -264,8 +270,8 @@ where where T: Serialize, { - fn next(&mut self, _state: &SerializerState) -> Option { - self.0.next().map(SerializeHandle::to) + fn next(&mut self, _state: &SerializerState) -> Result, Error> { + Ok(self.0.next().map(SerializeHandle::to)) } } @@ -289,8 +295,8 @@ where where T: Serialize, { - fn next(&mut self, _state: &SerializerState) -> Option { - self.0.next().map(SerializeHandle::to) + fn next(&mut self, _state: &SerializerState) -> Result, Error> { + Ok(self.0.next().map(SerializeHandle::to)) } } @@ -339,18 +345,18 @@ macro_rules! serialize_for_tuple { where $($name: Serialize,)* { - fn next(&mut self,_state: &SerializerState) -> Option { + fn next(&mut self,_state: &SerializerState) -> Result, Error> { let ($(ref $name,)*) = self.tuple; let __index = self.index; self.index += 1; let mut __counter = 0; $( if __index == __counter { - return Some(SerializeHandle::to($name)); + return Ok(Some(SerializeHandle::to($name))); } __counter += 1; )* - None + Ok(None) } } diff --git a/deser/src/ser/mod.rs b/deser/src/ser/mod.rs index aa8246b..d62cbd7 100644 --- a/deser/src/ser/mod.rs +++ b/deser/src/ser/mod.rs @@ -77,14 +77,16 @@ //! } //! //! impl<'a> StructEmitter for UserEmitter<'a> { -//! fn next(&mut self, _state: &SerializerState) -> Option<(Cow<'_, str>, SerializeHandle)> { +//! fn next(&mut self, _state: &SerializerState) +//! -> Result, SerializeHandle)>, Error> +//! { //! let index = self.index; //! self.index += 1; -//! match index { +//! Ok(match index { //! 0 => Some(("id".into(), SerializeHandle::to(&self.user.id))), //! 1 => Some(("username".into(), SerializeHandle::to(&self.user.username))), //! _ => None -//! } +//! }) //! } //! } //! ``` @@ -296,7 +298,7 @@ where Layer::Struct(ref mut s) => { // this is safe as we maintain our own stack. match unsafe { - extend_lifetime!(s.next(&state), Option<(Cow, SerializeHandle)>) + extend_lifetime!(s.next(&state)?, Option<(Cow, SerializeHandle)>) } { Some((key, value)) => { let key_descriptor = key.descriptor(); @@ -318,7 +320,7 @@ where *feed_value = !old_feed_value; if old_feed_value { let value = - unsafe { extend_lifetime!(m.next_value(&state), SerializeHandle) }; + unsafe { extend_lifetime!(m.next_value(&state)?, SerializeHandle) }; serializable = value; chunk = unsafe { extended_serializable!() }.serialize(&state)?; descriptor = unsafe { extended_serializable!() }.descriptor(); @@ -326,7 +328,7 @@ where } // this is safe as we maintain our own stack. match unsafe { - extend_lifetime!(m.next_key(&state), Option) + extend_lifetime!(m.next_key(&state)?, Option) } { Some(key) => { serializable = key; @@ -339,8 +341,9 @@ where } Layer::Seq(ref mut seq) => { // this is safe as we maintain our own stack. - match unsafe { extend_lifetime!(seq.next(&state), Option) } - { + match unsafe { + extend_lifetime!(seq.next(&state)?, Option) + } { Some(next) => { serializable = next; chunk = unsafe { extended_serializable!() }.serialize(&state)?; @@ -369,7 +372,10 @@ where /// it only knows about maps. pub trait StructEmitter { /// Produces the next field and value in the struct. - fn next(&mut self, state: &SerializerState) -> Option<(Cow<'_, str>, SerializeHandle)>; + fn next( + &mut self, + state: &SerializerState, + ) -> Result, SerializeHandle)>, Error>; } /// A map emitter. @@ -379,7 +385,7 @@ pub trait MapEmitter { /// If this reached the end of the map `None` shall be returned. The expectation /// is that this method changes an internal state in the emitter and the next /// call to [`next_value`](Self::next_value) returns the corresponding value. - fn next_key(&mut self, state: &SerializerState) -> Option; + fn next_key(&mut self, state: &SerializerState) -> Result, Error>; /// Produces the next value in the map. /// @@ -387,13 +393,13 @@ pub trait MapEmitter { /// /// This method shall panic if the emitter is not able to produce a value because /// the emitter is in the wrong state. - fn next_value(&mut self, state: &SerializerState) -> SerializeHandle; + fn next_value(&mut self, state: &SerializerState) -> Result; } /// A sequence emitter. pub trait SeqEmitter { /// Produces the next item in the sequence. - fn next(&mut self, state: &SerializerState) -> Option; + fn next(&mut self, state: &SerializerState) -> Result, Error>; } /// A data structure that can be serialized into any data format supported by Deser. diff --git a/deser/tests/test_custom_map.rs b/deser/tests/test_custom_map.rs index 52326b6..39c17c5 100644 --- a/deser/tests/test_custom_map.rs +++ b/deser/tests/test_custom_map.rs @@ -19,17 +19,17 @@ pub struct FlagsMapEmitter<'a> { } impl<'a> MapEmitter for FlagsMapEmitter<'a> { - fn next_key(&mut self, _state: &SerializerState) -> Option { - if let Some((key, value)) = self.iter.next() { + fn next_key(&mut self, _state: &SerializerState) -> Result, Error> { + Ok(if let Some((key, value)) = self.iter.next() { self.value = Some(value); Some(SerializeHandle::boxed(key.to_string())) } else { None - } + }) } - fn next_value(&mut self, _state: &SerializerState) -> SerializeHandle { - SerializeHandle::to(self.value.unwrap()) + fn next_value(&mut self, _state: &SerializerState) -> Result { + Ok(SerializeHandle::to(self.value.unwrap())) } } diff --git a/deser/tests/test_de_derive.rs b/deser/tests/test_de_derive.rs index 1e2b5a7..76e3f7f 100644 --- a/deser/tests/test_de_derive.rs +++ b/deser/tests/test_de_derive.rs @@ -358,3 +358,88 @@ fn test_variant_alias() { let s: Stuff = deserialize(vec!["alpha".into()]); assert_eq!(s, Stuff::A); } + +#[test] +fn test_flatten_basics() { + #[derive(Deserialize, PartialEq, Eq, Debug)] + struct Test { + a: usize, + b: usize, + #[deser(flatten)] + inner1: Inner1, + #[deser(flatten)] + inner2: Inner2, + c: usize, + } + + #[derive(Deserialize, PartialEq, Eq, Debug)] + struct Inner1 { + inner1_a: usize, + inner1_b: usize, + } + + #[derive(Deserialize, PartialEq, Eq, Debug)] + struct Inner2 { + inner2_a: usize, + inner2_b: usize, + } + + let s: Test = deserialize(vec![ + Event::MapStart, + "a".into(), + 1u64.into(), + "b".into(), + 2u64.into(), + "inner1_a".into(), + 99u64.into(), + "inner1_b".into(), + 100u64.into(), + "inner2_a".into(), + 199u64.into(), + "inner2_b".into(), + 200u64.into(), + "c".into(), + 3u64.into(), + Event::MapEnd, + ]); + + assert_eq!( + s, + Test { + a: 1, + b: 2, + c: 3, + inner1: Inner1 { + inner1_a: 99, + inner1_b: 100, + }, + inner2: Inner2 { + inner2_a: 199, + inner2_b: 200, + }, + } + ); +} + +#[test] +#[should_panic = "Missing field 'b'"] +fn test_flatten_incomplete_inner() { + #[derive(Deserialize, PartialEq, Eq, Debug)] + struct Test { + a: usize, + #[deser(flatten)] + inner: Inner, + } + + #[derive(Deserialize, PartialEq, Eq, Debug)] + struct Inner { + b: usize, + } + + let _: Test = deserialize(vec![ + Event::MapStart, + "a".into(), + 1u64.into(), + Event::MapEnd, + ]); +} diff --git a/deser/tests/test_ser_derive.rs b/deser/tests/test_ser_derive.rs index 4d71eb8..330906f 100644 --- a/deser/tests/test_ser_derive.rs +++ b/deser/tests/test_ser_derive.rs @@ -84,3 +84,157 @@ fn test_skip_serializing_optionals_some() { vec![Event::MapStart, "b".into(), 2u64.into(), Event::MapEnd] ); } + +#[test] +fn test_flatten_basics() { + #[derive(Serialize)] + struct Test { + a: usize, + b: usize, + #[deser(flatten)] + inner1: Inner1, + #[deser(flatten)] + inner2: Inner2, + c: usize, + } + + #[derive(Serialize)] + struct Inner1 { + inner1_a: usize, + inner1_b: usize, + } + + #[derive(Serialize)] + struct Inner2 { + inner2_a: usize, + inner2_b: usize, + } + + assert_eq!( + serialize(&Test { + a: 1, + b: 2, + c: 3, + inner1: Inner1 { + inner1_a: 99, + inner1_b: 100, + }, + inner2: Inner2 { + inner2_a: 199, + inner2_b: 200, + }, + }), + vec![ + Event::MapStart, + "a".into(), + 1u64.into(), + "b".into(), + 2u64.into(), + "inner1_a".into(), + 99u64.into(), + "inner1_b".into(), + 100u64.into(), + "inner2_a".into(), + 199u64.into(), + "inner2_b".into(), + 200u64.into(), + "c".into(), + 3u64.into(), + Event::MapEnd, + ] + ); +} + +#[test] +fn test_flatten_skip_optionals() { + #[derive(Serialize)] + #[deser(skip_serializing_optionals)] + struct Outer { + required: bool, + first: Option, + #[deser(flatten)] + inner: Inner, + } + + #[derive(Serialize)] + struct Inner { + second: Option, + } + + assert_eq!( + serialize(&Outer { + required: true, + first: None, + inner: Inner { second: None } + }), + vec![ + Event::MapStart, + "required".into(), + true.into(), + Event::MapEnd, + ] + ); + + assert_eq!( + serialize(&Outer { + required: true, + first: None, + inner: Inner { second: Some(111) } + }), + vec![ + Event::MapStart, + "required".into(), + true.into(), + "second".into(), + 111u64.into(), + Event::MapEnd, + ] + ); +} + +#[test] +fn test_flatten_skip_serializing_if() { + fn not_inner(inner: &Inner) -> bool { + inner.second == 42 + } + + #[derive(Serialize)] + struct Outer { + required: bool, + #[deser(flatten, skip_serializing_if = "not_inner")] + inner: Inner, + } + + #[derive(Serialize)] + struct Inner { + second: usize, + } + + assert_eq!( + serialize(&Outer { + required: true, + inner: Inner { second: 42 } + }), + vec![ + Event::MapStart, + "required".into(), + true.into(), + Event::MapEnd, + ] + ); + + assert_eq!( + serialize(&Outer { + required: true, + inner: Inner { second: 23 } + }), + vec![ + Event::MapStart, + "required".into(), + true.into(), + "second".into(), + 23u64.into(), + Event::MapEnd, + ] + ); +} diff --git a/examples/derive/src/main.rs b/examples/derive/src/main.rs index 0d110a7..1af3370 100644 --- a/examples/derive/src/main.rs +++ b/examples/derive/src/main.rs @@ -8,6 +8,15 @@ pub struct User { id: usize, email_address: String, kind: UserKind, + #[deser(flatten)] + user_attributes: UserAttributes, +} + +#[derive(Serialize, Deserialize)] +#[deser(rename_all = "camelCase")] +struct UserAttributes { + is_special: bool, + is_powerful: bool, } #[derive(Serialize, Deserialize)] @@ -28,6 +37,10 @@ fn main() { driver.emit("jane@example.com").unwrap(); driver.emit("kind").unwrap(); driver.emit("admin").unwrap(); + driver.emit("isPowerful").unwrap(); + driver.emit(true).unwrap(); + driver.emit("isSpecial").unwrap(); + driver.emit(true).unwrap(); driver.emit(Event::MapEnd).unwrap(); } println!("{:#?}", ToDebug::new(&user.unwrap())); diff --git a/examples/manual-struct/src/main.rs b/examples/manual-struct/src/main.rs index 90819b7..9ebf42b 100644 --- a/examples/manual-struct/src/main.rs +++ b/examples/manual-struct/src/main.rs @@ -37,17 +37,20 @@ struct UserEmitter<'a> { } impl<'a> StructEmitter for UserEmitter<'a> { - fn next(&mut self, _state: &SerializerState) -> Option<(Cow<'_, str>, SerializeHandle)> { + fn next( + &mut self, + _state: &SerializerState, + ) -> Result, SerializeHandle)>, Error> { let index = self.index; self.index += 1; - match index { + Ok(match index { 0 => Some((Cow::Borrowed("id"), SerializeHandle::to(&self.user.id))), 1 => Some(( Cow::Borrowed("emailAddress"), SerializeHandle::to(&self.user.email_address), )), _ => None, - } + }) } }