From f09ab98f17fb235f90d909bca3771bd2b283e0e2 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 29 Mar 2020 17:11:42 +0530 Subject: [PATCH] Remove manual Serialize impl, use derive macro instead --- ruma-events-macros/src/gen.rs | 107 +++------------------------------- src/direct.rs | 6 +- src/dummy.rs | 2 +- src/presence.rs | 4 +- src/room/member.rs | 16 ++--- 5 files changed, 22 insertions(+), 113 deletions(-) diff --git a/ruma-events-macros/src/gen.rs b/ruma-events-macros/src/gen.rs index 6e892fb9..fe1b6ac9 100644 --- a/ruma-events-macros/src/gen.rs +++ b/ruma-events-macros/src/gen.rs @@ -6,7 +6,7 @@ use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, - Attribute, Field, Ident, LitStr, Token, Type, + Attribute, Field, Ident, LitStr, Token, }; use crate::parse::{Content, EventKind, RumaEventInput}; @@ -89,7 +89,6 @@ impl ToTokens for RumaEvent { }; let name = &self.name; - let name_str = format!("{}", name); let content_docstring = format!("The payload for `{}`.", name); let content = match &self.content { @@ -126,60 +125,6 @@ impl ToTokens for RumaEvent { Content::Typedef(_) => TokenStream::new(), }; - // Only custom events will already have an event_type field, since we don't handle - // custom events we start at one. - let mut base_field_count: usize = 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 serialize_field_calls: Vec = Vec::with_capacity(event_fields.len()); - - for field in event_fields { - let ident = field.ident.clone().unwrap(); - - let ident_str = if ident == "event_type" { - "type".to_string() - } else { - format!("{}", ident) - }; - - let span = field.span(); - - // 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); - } - - 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 impl_room_event = match self.kind { EventKind::RoomEvent | EventKind::StateEvent => { quote! { @@ -269,42 +214,19 @@ impl ToTokens for RumaEvent { TokenStream::new() }; - let stripped_fields = event_fields - .iter() - .map(|event_field| strip_serde_attrs(event_field)); - + let event_type_name = self.event_type.value(); let output = quote!( #(#attrs)* - #[derive(Clone, PartialEq, Debug, ruma_events_macros::FromRaw)] + #[derive(Clone, PartialEq, Debug, serde::Serialize, ruma_events_macros::FromRaw)] + #[serde(rename = #event_type_name, tag = "type")] pub struct #name { - #(#stripped_fields),* + #(#event_fields),* } #content #impl_event_result_compatible_for_content - impl serde::Serialize for #name { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer - { - use serde::ser::SerializeStruct as _; - use ::ruma_events::Event as _; - - let mut len = #base_field_count; - - #(#increment_struct_len_statements)* - - let mut state = serializer.serialize_struct(#name_str, len)?; - - #(#serialize_field_calls)* - state.serialize_field("type", &self.event_type())?; - - state.end() - } - } - impl ::ruma_events::Event for #name { /// The type of this event's `content` field. type Content = #content_name; @@ -367,12 +289,14 @@ fn populate_room_event_fields(content_name: Ident, fields: Vec) -> Vec, /// The unique identifier for the user who sent this event. pub sender: ruma_identifiers::UserId, /// Additional key-value pairs not signed by the homeserver. + #[serde(skip_serializing_if = "Option::is_none")] pub unsigned: Option, }; @@ -387,6 +311,7 @@ fn populate_state_fields(content_name: Ident, fields: Vec) -> Vec let punctuated_fields: Punctuated = parse_quote! { /// The previous content for this state key, if any. + #[serde(skip_serializing_if = "Option::is_none")] pub prev_content: Option<#content_name>, /// A key that determines which piece of room state the event represents. @@ -398,15 +323,6 @@ fn populate_state_fields(content_name: Ident, fields: Vec) -> Vec fields } -/// 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().ident == "Option" - } else { - panic!("struct field had unexpected non-path type"); - } -} - /// Splits the given `event_type` string on `.` and `_` removing the `m.` then /// camel casing to give the `EventType` variant. fn to_camel_case(name: String) -> String { @@ -433,10 +349,3 @@ impl Parse for ParsableNamedField { Ok(Self { field }) } } - -/// Removes `serde` attributes from struct fields. -pub fn strip_serde_attrs(field: &Field) -> Field { - let mut field = field.clone(); - field.attrs.retain(|attr| !attr.path.is_ident("serde")); - field -} diff --git a/src/direct.rs b/src/direct.rs index ee9f9eff..8bdb54ab 100644 --- a/src/direct.rs +++ b/src/direct.rs @@ -43,7 +43,7 @@ mod tests { assert_eq!( to_string(&event).unwrap(), format!( - r#"{{"content":{{"{}":["{}"]}},"type":"m.direct"}}"#, + r#"{{"type":"m.direct","content":{{"{}":["{}"]}}}}"#, alice.to_string(), room[0].to_string() ) @@ -60,8 +60,8 @@ mod tests { let json_data = format!( r#"{{ - "content": {{ "{}": ["{}", "{}"] }}, - "type": "m.direct" + "type": "m.direct", + "content": {{ "{}": ["{}", "{}"] }} }}"#, alice.to_string(), rooms[0].to_string(), diff --git a/src/dummy.rs b/src/dummy.rs index 45239c69..e8592f73 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -34,7 +34,7 @@ mod tests { let dummy_event = DummyEvent { content: Empty }; let actual = serde_json::to_string(&dummy_event).unwrap(); - let expected = r#"{"content":{},"type":"m.dummy"}"#; + let expected = r#"{"type":"m.dummy","content":{}}"#; assert_eq!(actual, expected); } diff --git a/src/presence.rs b/src/presence.rs index b75b8a34..a9138164 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -96,7 +96,7 @@ mod tests { sender: UserId::try_from("@example:localhost").unwrap(), }; - let json = r#"{"content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"sender":"@example:localhost","type":"m.presence"}"#; + let json = r#"{"type":"m.presence","content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"sender":"@example:localhost"}"#; assert_eq!(to_string(&event).unwrap(), json); } @@ -115,7 +115,7 @@ mod tests { sender: UserId::try_from("@example:localhost").unwrap(), }; - let json = r#"{"content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"sender":"@example:localhost","type":"m.presence"}"#; + let json = r#"{"type":"m.presence","content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"sender":"@example:localhost"}"#; assert_eq!( serde_json::from_str::>(json) diff --git a/src/room/member.rs b/src/room/member.rs index 69b8148f..00bd5a21 100644 --- a/src/room/member.rs +++ b/src/room/member.rs @@ -249,6 +249,7 @@ mod tests { prev_content: None, }; let json = json!({ + "type": "m.room.member", "content": { "membership": "join" }, @@ -256,8 +257,7 @@ mod tests { "origin_server_ts": 1, "room_id": "!n8f893n9:example.com", "sender": "@carl:example.com", - "state_key": "example.com", - "type": "m.room.member" + "state_key": "example.com" }); serde_json_eq_try_from_raw(event, json); } @@ -287,6 +287,7 @@ mod tests { }), }; let json = json!({ + "type": "m.room.member", "content": { "membership": "join" }, @@ -297,8 +298,7 @@ mod tests { }, "room_id": "!n8f893n9:example.com", "sender": "@carl:example.com", - "state_key": "example.com", - "type": "m.room.member" + "state_key": "example.com" }); serde_json_eq_try_from_raw(event, json); } @@ -337,6 +337,7 @@ mod tests { prev_content: None, }; let json = json!({ + "type": "m.room.member", "content": { "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", "displayname": "Alice Margatroid", @@ -359,8 +360,7 @@ mod tests { "origin_server_ts":233, "room_id": "!jEsUZKDJdhlrceRyVU:example.org", "sender": "@alice:example.org", - "state_key": "@alice:example.org", - "type": "m.room.member" + "state_key": "@alice:example.org" }); serde_json_eq_try_from_raw(event, json); } @@ -405,6 +405,7 @@ mod tests { }), }; let json = json!({ + "type": "m.room.member", "content": { "membership": "join" }, @@ -430,8 +431,7 @@ mod tests { }, "room_id": "!jEsUZKDJdhlrceRyVU:example.org", "sender": "@alice:example.org", - "state_key": "@alice:example.org", - "type": "m.room.member" + "state_key": "@alice:example.org" }); serde_json_eq_try_from_raw(event, json); }