From e34ea0544521e97ec96aa5a4f5a2c4e5e985653a Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Fri, 21 Jun 2019 16:33:53 -0700 Subject: [PATCH] Skip serializing options. --- src/gen.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/gen.rs b/src/gen.rs index 0b205d0f..21b8e6e5 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -7,7 +7,7 @@ use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, - Attribute, Field, Ident, Path, Token, + Attribute, Field, Ident, Path, Token, Type, }; use crate::parse::{Content, EventKind, RumaEventInput}; @@ -142,8 +142,11 @@ impl ToTokens for RumaEvent { // Custom events will already have an event_type field. All other events need to account // for this field being manually inserted in `Serialize` impls. - let event_type_field_count = if self.is_custom { 0 } else { 1 }; - let field_count = event_fields.len() + event_type_field_count; + let mut base_field_count: usize = if self.is_custom { 0 } else { 1 }; + + // Keep track of all the optional fields, because we'll need to check at runtime if they + // are `Some` in order to increase the number of fields we tell serde to serialize. + let mut optional_field_idents = Vec::with_capacity(event_fields.len()); let mut try_from_field_values: Vec = Vec::with_capacity(event_fields.len()); let mut serialize_field_calls: Vec = Vec::with_capacity(event_fields.len()); @@ -227,8 +230,21 @@ impl ToTokens for RumaEvent { try_from_field_values.push(try_from_field_value); - let serialize_field_call = quote_spanned! {span=> - state.serialize_field(#ident_str, &self.#ident)?; + // Does the same thing as #[serde(skip_serializing_if = "Option::is_none")] + let serialize_field_call = if is_option(&field.ty) { + optional_field_idents.push(ident.clone()); + + quote_spanned! {span=> + if self.#ident.is_some() { + state.serialize_field(#ident_str, &self.#ident)?; + } + } + } else { + base_field_count += 1; + + quote_spanned! {span=> + state.serialize_field(#ident_str, &self.#ident)?; + } }; serialize_field_calls.push(serialize_field_call); @@ -251,6 +267,27 @@ impl ToTokens for RumaEvent { ) }; + let increment_struct_len_statements: Vec = optional_field_idents + .iter() + .map(|ident| { + let span = ident.span(); + + quote_spanned! {span=> + if self.#ident.is_some() { + len += 1; + } + } + }) + .collect(); + + let set_up_struct_serializer = quote! { + let mut len = #base_field_count; + + #(#increment_struct_len_statements)* + + let mut state = serializer.serialize_struct(#name_str, len)?; + }; + let impl_room_event = match self.kind { EventKind::RoomEvent | EventKind::StateEvent => { quote! { @@ -347,7 +384,7 @@ impl ToTokens for RumaEvent { { #import_event_in_serialize_impl - let mut state = serializer.serialize_struct(#name_str, #field_count)?; + #set_up_struct_serializer #(#serialize_field_calls)* #manually_serialize_type_field @@ -490,6 +527,15 @@ fn is_custom_event_type(event_type: &Path) -> bool { event_type.segments.last().unwrap().value().ident == "Custom" } +/// Checks if a type is an `Option`. +fn is_option(ty: &Type) -> bool { + if let Type::Path(ref type_path) = ty { + type_path.path.segments.first().unwrap().value().ident == "Option" + } else { + panic!("struct field had unexpected non-path type"); + } +} + /// A wrapper around `syn::Field` that makes it possible to parse `Punctuated` /// from a `TokenStream`. ///