From 0b825ef38b9167ff2d52490c6d5d80e8eb861e44 Mon Sep 17 00:00:00 2001 From: Devin R Date: Sat, 13 Jun 2020 19:33:54 -0400 Subject: [PATCH 1/5] Adds derive to generate custom Deserialize impl for top level any event enums --- ruma-events-macros/Cargo.toml | 2 +- ruma-events-macros/src/any_deserialize.rs | 111 ++++++++ ruma-events-macros/src/lib.rs | 9 + ruma-events/src/enums.rs | 300 +++++++++++++++++++++- 4 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 ruma-events-macros/src/any_deserialize.rs diff --git a/ruma-events-macros/Cargo.toml b/ruma-events-macros/Cargo.toml index ad801476ab..98630ec5d5 100644 --- a/ruma-events-macros/Cargo.toml +++ b/ruma-events-macros/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/ruma/ruma-api-macros" version = "0.21.3" [dependencies] -syn = { version = "1.0.25", features = ["full"] } +syn = { version = "1.0.25", features = ["full", "extra-traits"] } quote = "1.0.6" proc-macro2 = "1.0.17" diff --git a/ruma-events-macros/src/any_deserialize.rs b/ruma-events-macros/src/any_deserialize.rs new file mode 100644 index 0000000000..20e4b8e514 --- /dev/null +++ b/ruma-events-macros/src/any_deserialize.rs @@ -0,0 +1,111 @@ +//! Implementation of the top level `Any*Event` derive macro. +//! +//! This is just a custom `Deserialize` impl made into a derive to avoid +//! code duplication. + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{Data, DataEnum, DeriveInput, Ident}; + +/// Derive `AnyEventDeserialize` macro code generation. +pub fn expand_any_event_deserialize(input: DeriveInput) -> syn::Result { + let ident = &input.ident; + let variant_idents = if let Data::Enum(DataEnum { variants, .. }) = input.data.clone() { + variants.into_iter().map(|v| v.ident).collect::>() + } else { + return Err(syn::Error::new( + Span::call_site(), + "the `AnyEventDeserialize` derive only supports enums", + )); + }; + + let match_block = variant_idents.iter().map(match_event_type).collect::>(); + + let deserialize_impl = quote! { + impl<'de> ::serde::de::Deserialize<'de> for #ident { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::de::Deserializer<'de>, + { + use ::serde::de::Error as _; + + let json = ::serde_json::Value::deserialize(deserializer)?; + let ev_type: String = ::ruma_events::util::get_field(&json, "type")?; + + match ev_type.as_str() { + #( #match_block )* + _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), + } + } + } + }; + + Ok(quote! { + #deserialize_impl + }) +} + +/// Match the variant name with the grouping of events and return the deserialized event +/// wrapped in the correct variant. +fn match_event_type(variant: &Ident) -> TokenStream { + let deserialize_event = quote! { + Ok(Self::#variant(::serde_json::from_value(json).map_err(D::Error::custom)?)) + }; + match variant.to_string().as_str() { + "Basic" => quote! { + "m.direct" + | "m.dummy" + | "m.ignored_user_list" + | "m.push_rules" + | "m.room_key" + | "m.tag" => { + #deserialize_event + } + }, + "Presence" => quote! { + "m.presence" => #deserialize_event, + }, + "Redaction" => quote! { + "m.room.redaction" => #deserialize_event, + }, + "Ephemeral" => quote! { + "m.fully_read" | "m.receipt" | "m.typing" => { + #deserialize_event + } + }, + "Message" => quote! { + "m.call.answer" + | "m.call.invite" + | "m.call.hangup" + | "m.call.candidates" + | "m.room.encrypted" + | "m.room.message" + | "m.room.message.feedback" + | "m.sticker" => { + #deserialize_event + } + }, + "State" | "StateEvent" => quote! { + "m.room.aliases" + | "m.room.avatar" + | "m.room.canonical_alias" + | "m.room.create" + | "m.room.encryption" + | "m.room.guest_access" + | "m.room.history_visibility" + | "m.room.join_rules" + | "m.room.member" + | "m.room.name" + | "m.room.pinned_events" + | "m.room.power_levels" + | "m.room.redaction" + | "m.room.server_acl" + | "m.room.third_party_invite" + | "m.room.tombstone" + | "m.room.topic" => { + #deserialize_event + } + }, + _ => TokenStream::new(), + } +} diff --git a/ruma-events-macros/src/lib.rs b/ruma-events-macros/src/lib.rs index 84b4a5e151..3eea7d717f 100644 --- a/ruma-events-macros/src/lib.rs +++ b/ruma-events-macros/src/lib.rs @@ -11,6 +11,7 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; use self::{ + any_deserialize::expand_any_event_deserialize, content_enum::{expand_content_enum, ContentEnumInput}, event::expand_event, event_content::{ @@ -19,6 +20,7 @@ use self::{ }, }; +mod any_deserialize; mod content_enum; mod event; mod event_content; @@ -81,3 +83,10 @@ pub fn derive_state_event(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand_event(input).unwrap_or_else(|err| err.to_compile_error()).into() } + +/// Generates implementations needed to serialize and deserialize Matrix events. +#[proc_macro_derive(AnyEventDeserialize)] +pub fn derive_any_event_deserialize(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + expand_any_event_deserialize(input).unwrap_or_else(|err| err.to_compile_error()).into() +} diff --git a/ruma-events/src/enums.rs b/ruma-events/src/enums.rs index fb0fcabe28..275105e7d2 100644 --- a/ruma-events/src/enums.rs +++ b/ruma-events/src/enums.rs @@ -1,5 +1,5 @@ -use ruma_events_macros::event_content_enum; -use serde::{Deserialize, Serialize}; +use ruma_events_macros::{event_content_enum, AnyEventDeserialize}; +use serde::Serialize; use crate::{ event_kinds::{ @@ -116,25 +116,25 @@ pub type AnyStrippedStateEventStub = StrippedStateEventStub; /// Any event. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] #[serde(untagged)] pub enum AnyEvent { /// Any basic event. - Basic(AnyBasicEvent), + Basic(BasicEvent), /// `"m.presence"`, the only non-room event with a `sender` field. Presence(PresenceEvent), /// Any ephemeral room event. - Ephemeral(AnyEphemeralRoomEvent), + Ephemeral(EphemeralRoomEvent), /// Any message event. - Message(AnyMessageEvent), + Message(MessageEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. - State(AnyStateEvent), + State(StateEvent), } /// Any room event. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] #[serde(untagged)] pub enum AnyRoomEvent { /// Any message event. @@ -146,7 +146,7 @@ pub enum AnyRoomEvent { } /// Any room event stub (room event without a `room_id`, as returned in `/sync` responses) -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] #[serde(untagged)] pub enum AnyRoomEventStub { /// Any message event stub @@ -156,3 +156,285 @@ pub enum AnyRoomEventStub { /// Any state event stub StateEvent(AnyStateEventStub), } + +#[cfg(test)] +mod test { + use std::convert::TryFrom; + + use matches::assert_matches; + use ruma_identifiers::RoomAliasId; + use serde_json::{from_value as from_json_value, json}; + + use crate::{ + room::{ + aliases::AliasesEventContent, + message::{MessageEventContent, TextMessageEventContent}, + power_levels::PowerLevelsEventContent, + }, + AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent, + EventJson, MessageEvent, MessageEventStub, StateEvent, StateEventStub, + }; + + #[test] + fn power_event_stub_deserialization() { + let json_data = json!({ + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 45, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 45 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::StateEvent( + StateEventStub { + content: AnyStateEventContent::RoomPowerLevels(PowerLevelsEventContent { + ban, .. + }), + .. + } + ) + if ban == js_int::Int::new(50).unwrap() + ); + } + + #[test] + fn message_event_stub_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::Message( + MessageEventStub { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); + } + + #[test] + fn aliases_event_stub_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::StateEvent( + StateEventStub { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); + } + + #[test] + fn message_room_event_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "room_id": "!room:room.com", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEvent::Message( + MessageEvent { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); + } + + #[test] + fn alias_room_event_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ "#somewhere:localhost" ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "room_id": "!room:room.com", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEvent::State( + StateEvent { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); + } + + #[test] + fn message_event_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "room_id": "!room:room.com", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyEvent::Message( + MessageEvent { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); + } + + #[test] + fn alias_event_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ "#somewhere:localhost" ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "room_id": "!room:room.com", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyEvent::State( + StateEvent { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); + } +} From fafc58429a09684b1cd7c880768354eba46eaa17 Mon Sep 17 00:00:00 2001 From: Devin R Date: Sat, 13 Jun 2020 19:53:23 -0400 Subject: [PATCH 2/5] Adds tests for the AnyEventDeserialize derive macro, moves enums.rs test to seperate file --- ruma-events-macros/Cargo.toml | 2 +- ruma-events-macros/src/any_deserialize.rs | 53 ++- ruma-events-macros/src/content_enum.rs | 44 ++- ruma-events-macros/src/lib.rs | 2 +- ruma-events/src/enums.rs | 314 ++---------------- ruma-events/tests/any_deserialize_derive.rs | 6 + ruma-events/tests/any_enums.rs | 278 ++++++++++++++++ .../ui/09-any-deserialize-sanity-check.rs | 10 + ruma-events/tests/ui/10-invalid-variant.rs | 10 + .../tests/ui/10-invalid-variant.stderr | 7 + 10 files changed, 431 insertions(+), 295 deletions(-) create mode 100644 ruma-events/tests/any_deserialize_derive.rs create mode 100644 ruma-events/tests/any_enums.rs create mode 100644 ruma-events/tests/ui/09-any-deserialize-sanity-check.rs create mode 100644 ruma-events/tests/ui/10-invalid-variant.rs create mode 100644 ruma-events/tests/ui/10-invalid-variant.stderr diff --git a/ruma-events-macros/Cargo.toml b/ruma-events-macros/Cargo.toml index 98630ec5d5..ad801476ab 100644 --- a/ruma-events-macros/Cargo.toml +++ b/ruma-events-macros/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/ruma/ruma-api-macros" version = "0.21.3" [dependencies] -syn = { version = "1.0.25", features = ["full", "extra-traits"] } +syn = { version = "1.0.25", features = ["full"] } quote = "1.0.6" proc-macro2 = "1.0.17" diff --git a/ruma-events-macros/src/any_deserialize.rs b/ruma-events-macros/src/any_deserialize.rs index 20e4b8e514..480b56ed22 100644 --- a/ruma-events-macros/src/any_deserialize.rs +++ b/ruma-events-macros/src/any_deserialize.rs @@ -19,7 +19,11 @@ pub fn expand_any_event_deserialize(input: DeriveInput) -> syn::Result>(); + let match_block = + variant_idents.iter().map(match_event_type).collect::>>()?; + + let content_type = + variant_idents.iter().map(any_enum_variant).collect::>>()?; let deserialize_impl = quote! { impl<'de> ::serde::de::Deserialize<'de> for #ident { @@ -45,13 +49,43 @@ pub fn expand_any_event_deserialize(input: DeriveInput) -> syn::Result syn::Result { + let tkns = match variant.to_string().as_str() { + "Basic" => quote! { + AnyBasicEventContent + }, + "Presence" => quote! { + PresenceEventContent + }, + "Redaction" => quote! { + RedactionEventContent + }, + "Ephemeral" => quote! { + AnyEphemeralRoomEventContent + }, + "Message" => quote! { + AnyMessageEventContent + }, + "State" => quote! { + AnyStateEventContent + }, + _ => { + return Err(syn::Error::new( + Span::call_site(), + format!("found unrecognized enum variant `{}`", variant), + )) + } + }; + Ok(tkns) +} + /// Match the variant name with the grouping of events and return the deserialized event /// wrapped in the correct variant. -fn match_event_type(variant: &Ident) -> TokenStream { +fn match_event_type(variant: &Ident) -> syn::Result { let deserialize_event = quote! { Ok(Self::#variant(::serde_json::from_value(json).map_err(D::Error::custom)?)) }; - match variant.to_string().as_str() { + let tkns = match variant.to_string().as_str() { "Basic" => quote! { "m.direct" | "m.dummy" @@ -85,7 +119,7 @@ fn match_event_type(variant: &Ident) -> TokenStream { #deserialize_event } }, - "State" | "StateEvent" => quote! { + "State" => quote! { "m.room.aliases" | "m.room.avatar" | "m.room.canonical_alias" @@ -106,6 +140,13 @@ fn match_event_type(variant: &Ident) -> TokenStream { #deserialize_event } }, - _ => TokenStream::new(), - } + _ => { + return Err(syn::Error::new( + Span::call_site(), + format!("found unrecognized enum variant `{}`", variant), + )) + } + }; + + Ok(tkns) } diff --git a/ruma-events-macros/src/content_enum.rs b/ruma-events-macros/src/content_enum.rs index a87742acf3..8bef946607 100644 --- a/ruma-events-macros/src/content_enum.rs +++ b/ruma-events-macros/src/content_enum.rs @@ -1,6 +1,6 @@ //! Implementation of the content_enum type macro. -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ parse::{self, Parse, ParseStream}, @@ -8,9 +8,19 @@ use syn::{ }; /// Create a content enum from `ContentEnumInput`. +/// +/// The is the internals of the `event_content_enum!` macro. pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result { let attrs = &input.attrs; let ident = &input.name; + + let ident_chars = ident.to_string().chars().collect::>(); + let event_enum = match ident_chars.as_slice() { + ['A', 'n', 'y', event @ .., 'C', 'o', 'n', 't', 'e', 'n', 't'] => event, + _ => return Err(syn::Error::new(Span::call_site(), "failure")), + }; + + let event_enum_ident = Ident::new(&event_enum.iter().collect::(), ident.span()); let event_type_str = &input.events; let variants = input.events.iter().map(to_camel_case).collect::>(); @@ -51,17 +61,49 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result } }; + let any_event_variant_impl = if let Some(variant) = any_enum_variant(ident) { + quote! { + impl #ident { + fn any_event_variant(&self) -> fn(#event_enum_ident) -> ::ruma_events::AnyEvent { + ::ruma_events::AnyEvent::#variant + } + } + } + } else { + TokenStream::new() + }; + let marker_trait_impls = marker_traits(ident); Ok(quote! { #content_enum + #any_event_variant_impl + #event_content_impl #marker_trait_impls }) } +fn any_enum_variant(ident: &Ident) -> Option { + match ident.to_string().as_str() { + "AnyStateEventContent" => Some(quote! { + State + }), + "AnyMessageEventContent" => Some(quote! { + Message + }), + "AnyEphemeralRoomEventContent" => Some(quote! { + Ephemeral + }), + "AnyBasicEventContent" => Some(quote! { + Basic + }), + _ => None, + } +} + fn marker_traits(ident: &Ident) -> TokenStream { match ident.to_string().as_str() { "AnyStateEventContent" => quote! { diff --git a/ruma-events-macros/src/lib.rs b/ruma-events-macros/src/lib.rs index 3eea7d717f..36d57ea9ed 100644 --- a/ruma-events-macros/src/lib.rs +++ b/ruma-events-macros/src/lib.rs @@ -84,7 +84,7 @@ pub fn derive_state_event(input: TokenStream) -> TokenStream { expand_event(input).unwrap_or_else(|err| err.to_compile_error()).into() } -/// Generates implementations needed to serialize and deserialize Matrix events. +/// Generates custom `Deserialize implementation for the any event enums. #[proc_macro_derive(AnyEventDeserialize)] pub fn derive_any_event_deserialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/ruma-events/src/enums.rs b/ruma-events/src/enums.rs index 275105e7d2..499509700e 100644 --- a/ruma-events/src/enums.rs +++ b/ruma-events/src/enums.rs @@ -1,5 +1,8 @@ use ruma_events_macros::{event_content_enum, AnyEventDeserialize}; -use serde::Serialize; +use serde::{de, Serialize}; +use serde_json::{ + from_value as from_json_value, value::RawValue as RawJsonValue, Value as JsonValue, +}; use crate::{ event_kinds::{ @@ -8,6 +11,7 @@ use crate::{ }, presence::PresenceEvent, room::redaction::{RedactionEvent, RedactionEventStub}, + util, }; event_content_enum! { @@ -116,21 +120,21 @@ pub type AnyStrippedStateEventStub = StrippedStateEventStub; /// Any event. -#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] +#[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyEvent { /// Any basic event. - Basic(BasicEvent), + Basic(AnyBasicEvent), /// `"m.presence"`, the only non-room event with a `sender` field. Presence(PresenceEvent), /// Any ephemeral room event. - Ephemeral(EphemeralRoomEvent), + Ephemeral(AnyEphemeralRoomEvent), /// Any message event. - Message(MessageEvent), + Message(AnyMessageEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. - State(StateEvent), + State(AnyStateEvent), } /// Any room event. @@ -154,287 +158,25 @@ pub enum AnyRoomEventStub { /// `"m.room.redaction"` stub Redaction(RedactionEventStub), /// Any state event stub - StateEvent(AnyStateEventStub), + State(AnyStateEventStub), } -#[cfg(test)] -mod test { - use std::convert::TryFrom; - - use matches::assert_matches; - use ruma_identifiers::RoomAliasId; - use serde_json::{from_value as from_json_value, json}; - - use crate::{ - room::{ - aliases::AliasesEventContent, - message::{MessageEventContent, TextMessageEventContent}, - power_levels::PowerLevelsEventContent, - }, - AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent, - EventJson, MessageEvent, MessageEventStub, StateEvent, StateEventStub, - }; - - #[test] - fn power_event_stub_deserialization() { - let json_data = json!({ - "content": { - "ban": 50, - "events": { - "m.room.avatar": 50, - "m.room.canonical_alias": 50, - "m.room.history_visibility": 100, - "m.room.name": 50, - "m.room.power_levels": 100 - }, - "events_default": 0, - "invite": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - "@example:localhost": 100 - }, - "users_default": 0 - }, - "event_id": "$15139375512JaHAW:localhost", - "origin_server_ts": 45, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.power_levels", - "unsigned": { - "age": 45 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::StateEvent( - StateEventStub { - content: AnyStateEventContent::RoomPowerLevels(PowerLevelsEventContent { - ban, .. - }), - .. - } - ) - if ban == js_int::Int::new(50).unwrap() - ); - } - - #[test] - fn message_event_stub_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::Message( - MessageEventStub { - content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { - body, - formatted: Some(formatted), - relates_to: None, - })), - .. - } - ) - if body == "baba" && formatted.body == "baba" - ); - } - - #[test] - fn aliases_event_stub_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::StateEvent( - StateEventStub { - content: AnyStateEventContent::RoomAliases(AliasesEventContent { - aliases, - }), - .. - } - ) - if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] - ); - } - - #[test] - fn message_room_event_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "room_id": "!room:room.com", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEvent::Message( - MessageEvent { - content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { - body, - formatted: Some(formatted), - relates_to: None, - })), - .. - } - ) - if body == "baba" && formatted.body == "baba" - ); - } - - #[test] - fn alias_room_event_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ "#somewhere:localhost" ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "room_id": "!room:room.com", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEvent::State( - StateEvent { - content: AnyStateEventContent::RoomAliases(AliasesEventContent { - aliases, - }), - .. - } - ) - if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] - ); - } - - #[test] - fn message_event_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "room_id": "!room:room.com", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyEvent::Message( - MessageEvent { - content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { - body, - formatted: Some(formatted), - relates_to: None, - })), - .. - } - ) - if body == "baba" && formatted.body == "baba" - ); - } - - #[test] - fn alias_event_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ "#somewhere:localhost" ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "room_id": "!room:room.com", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); - - assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyEvent::State( - StateEvent { - content: AnyStateEventContent::RoomAliases(AliasesEventContent { - aliases, - }), - .. - } - ) - if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] - ); +impl<'de> de::Deserialize<'de> for AnyEvent { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + use de::Error as _; + + let json = JsonValue::deserialize(deserializer)?; + let raw: Box = util::get_field(&json, "content")?; + let ev_type: String = util::get_field(&json, "type")?; + let content: AnyStateEventContent = + ruma_events::EventContent::from_parts(&ev_type, raw).map_err(D::Error::custom)?; + + match ev_type.as_str() { + "hello" => Ok(AnyEvent::Message(from_json_value(json).map_err(D::Error::custom)?)), + _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), + } } } diff --git a/ruma-events/tests/any_deserialize_derive.rs b/ruma-events/tests/any_deserialize_derive.rs new file mode 100644 index 0000000000..4df505d7d1 --- /dev/null +++ b/ruma-events/tests/any_deserialize_derive.rs @@ -0,0 +1,6 @@ +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.pass("tests/ui/09-any-deserialize-sanity-check.rs"); + t.compile_fail("tests/ui/10-invalid-variant.rs"); +} diff --git a/ruma-events/tests/any_enums.rs b/ruma-events/tests/any_enums.rs new file mode 100644 index 0000000000..71291be96b --- /dev/null +++ b/ruma-events/tests/any_enums.rs @@ -0,0 +1,278 @@ +use std::convert::TryFrom; + +use matches::assert_matches; +use ruma_identifiers::RoomAliasId; +use serde_json::{from_value as from_json_value, json}; + +use ruma_events::{ + room::{ + aliases::AliasesEventContent, + message::{MessageEventContent, TextMessageEventContent}, + power_levels::PowerLevelsEventContent, + }, + AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent, + EventJson, MessageEvent, MessageEventStub, StateEvent, StateEventStub, +}; + +#[test] +fn power_event_stub_deserialization() { + let json_data = json!({ + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 45, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 45 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::State( + StateEventStub { + content: AnyStateEventContent::RoomPowerLevels(PowerLevelsEventContent { + ban, .. + }), + .. + } + ) + if ban == js_int::Int::new(50).unwrap() + ); +} + +#[test] +fn message_event_stub_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::Message( + MessageEventStub { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); +} + +#[test] +fn aliases_event_stub_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEventStub::State( + StateEventStub { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); +} + +#[test] +fn message_room_event_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "room_id": "!room:room.com", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEvent::Message( + MessageEvent { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); +} + +#[test] +fn alias_room_event_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ "#somewhere:localhost" ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "room_id": "!room:room.com", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyRoomEvent::State( + StateEvent { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); +} + +#[test] +fn message_event_deserialization() { + let json_data = json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "room_id": "!room:room.com", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyEvent::Message( + MessageEvent { + content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { + body, + formatted: Some(formatted), + relates_to: None, + })), + .. + } + ) + if body == "baba" && formatted.body == "baba" + ); +} + +#[test] +fn alias_event_deserialization() { + let json_data = json!({ + "content": { + "aliases": [ "#somewhere:localhost" ] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "room_id": "!room:room.com", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize() + .unwrap(), + AnyEvent::State( + StateEvent { + content: AnyStateEventContent::RoomAliases(AliasesEventContent { + aliases, + }), + .. + } + ) + if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] + ); +} diff --git a/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs b/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs new file mode 100644 index 0000000000..a738aae4ba --- /dev/null +++ b/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs @@ -0,0 +1,10 @@ +use ruma_events_macros::AnyEventDeserialize; + +#[derive(Clone, Debug, AnyEventDeserialize)] +pub enum AnyRoomEventStub { + Message(()), + Redaction(()), + StateEvent(()), +} + +fn main() {} diff --git a/ruma-events/tests/ui/10-invalid-variant.rs b/ruma-events/tests/ui/10-invalid-variant.rs new file mode 100644 index 0000000000..ab7be3ef5c --- /dev/null +++ b/ruma-events/tests/ui/10-invalid-variant.rs @@ -0,0 +1,10 @@ +use ruma_events_macros::AnyEventDeserialize; + +#[derive(Clone, Debug, AnyEventDeserialize)] +pub enum AnyRoomEventStub { + Message(()), + Redaction(()), + NotAnEvent(()), +} + +fn main() {} diff --git a/ruma-events/tests/ui/10-invalid-variant.stderr b/ruma-events/tests/ui/10-invalid-variant.stderr new file mode 100644 index 0000000000..c7a47222f5 --- /dev/null +++ b/ruma-events/tests/ui/10-invalid-variant.stderr @@ -0,0 +1,7 @@ +error: found unrecognized enum variant `NotAnEvent` + --> $DIR/10-invalid-variant.rs:3:24 + | +3 | #[derive(Clone, Debug, AnyEventDeserialize)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) From ca69ed934d3c051e9cdb580955063faf4a1b8e35 Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 15 Jun 2020 13:54:59 -0400 Subject: [PATCH 3/5] Implement Deserialize manually for Any*Event enums --- .gitignore | 2 +- ruma-events-macros/src/any_deserialize.rs | 152 ------------------ ruma-events-macros/src/content_enum.rs | 40 +---- ruma-events-macros/src/lib.rs | 9 -- ruma-events/Cargo.toml | 5 + ruma-events/benches/event_deserialize.rs | 130 +++++++++++++++ ruma-events/src/enums.rs | 87 ++++++++-- ruma-events/tests/any_deserialize_derive.rs | 6 - .../ui/09-any-deserialize-sanity-check.rs | 10 -- ruma-events/tests/ui/10-invalid-variant.rs | 10 -- .../tests/ui/10-invalid-variant.stderr | 7 - 11 files changed, 217 insertions(+), 241 deletions(-) delete mode 100644 ruma-events-macros/src/any_deserialize.rs create mode 100644 ruma-events/benches/event_deserialize.rs delete mode 100644 ruma-events/tests/any_deserialize_derive.rs delete mode 100644 ruma-events/tests/ui/09-any-deserialize-sanity-check.rs delete mode 100644 ruma-events/tests/ui/10-invalid-variant.rs delete mode 100644 ruma-events/tests/ui/10-invalid-variant.stderr diff --git a/.gitignore b/.gitignore index 96ef6c0b94..a9d37c560c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/target +target Cargo.lock diff --git a/ruma-events-macros/src/any_deserialize.rs b/ruma-events-macros/src/any_deserialize.rs deleted file mode 100644 index 480b56ed22..0000000000 --- a/ruma-events-macros/src/any_deserialize.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Implementation of the top level `Any*Event` derive macro. -//! -//! This is just a custom `Deserialize` impl made into a derive to avoid -//! code duplication. - -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use syn::{Data, DataEnum, DeriveInput, Ident}; - -/// Derive `AnyEventDeserialize` macro code generation. -pub fn expand_any_event_deserialize(input: DeriveInput) -> syn::Result { - let ident = &input.ident; - let variant_idents = if let Data::Enum(DataEnum { variants, .. }) = input.data.clone() { - variants.into_iter().map(|v| v.ident).collect::>() - } else { - return Err(syn::Error::new( - Span::call_site(), - "the `AnyEventDeserialize` derive only supports enums", - )); - }; - - let match_block = - variant_idents.iter().map(match_event_type).collect::>>()?; - - let content_type = - variant_idents.iter().map(any_enum_variant).collect::>>()?; - - let deserialize_impl = quote! { - impl<'de> ::serde::de::Deserialize<'de> for #ident { - fn deserialize(deserializer: D) -> Result - where - D: ::serde::de::Deserializer<'de>, - { - use ::serde::de::Error as _; - - let json = ::serde_json::Value::deserialize(deserializer)?; - let ev_type: String = ::ruma_events::util::get_field(&json, "type")?; - - match ev_type.as_str() { - #( #match_block )* - _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), - } - } - } - }; - - Ok(quote! { - #deserialize_impl - }) -} - -fn any_enum_variant(variant: &Ident) -> syn::Result { - let tkns = match variant.to_string().as_str() { - "Basic" => quote! { - AnyBasicEventContent - }, - "Presence" => quote! { - PresenceEventContent - }, - "Redaction" => quote! { - RedactionEventContent - }, - "Ephemeral" => quote! { - AnyEphemeralRoomEventContent - }, - "Message" => quote! { - AnyMessageEventContent - }, - "State" => quote! { - AnyStateEventContent - }, - _ => { - return Err(syn::Error::new( - Span::call_site(), - format!("found unrecognized enum variant `{}`", variant), - )) - } - }; - Ok(tkns) -} - -/// Match the variant name with the grouping of events and return the deserialized event -/// wrapped in the correct variant. -fn match_event_type(variant: &Ident) -> syn::Result { - let deserialize_event = quote! { - Ok(Self::#variant(::serde_json::from_value(json).map_err(D::Error::custom)?)) - }; - let tkns = match variant.to_string().as_str() { - "Basic" => quote! { - "m.direct" - | "m.dummy" - | "m.ignored_user_list" - | "m.push_rules" - | "m.room_key" - | "m.tag" => { - #deserialize_event - } - }, - "Presence" => quote! { - "m.presence" => #deserialize_event, - }, - "Redaction" => quote! { - "m.room.redaction" => #deserialize_event, - }, - "Ephemeral" => quote! { - "m.fully_read" | "m.receipt" | "m.typing" => { - #deserialize_event - } - }, - "Message" => quote! { - "m.call.answer" - | "m.call.invite" - | "m.call.hangup" - | "m.call.candidates" - | "m.room.encrypted" - | "m.room.message" - | "m.room.message.feedback" - | "m.sticker" => { - #deserialize_event - } - }, - "State" => quote! { - "m.room.aliases" - | "m.room.avatar" - | "m.room.canonical_alias" - | "m.room.create" - | "m.room.encryption" - | "m.room.guest_access" - | "m.room.history_visibility" - | "m.room.join_rules" - | "m.room.member" - | "m.room.name" - | "m.room.pinned_events" - | "m.room.power_levels" - | "m.room.redaction" - | "m.room.server_acl" - | "m.room.third_party_invite" - | "m.room.tombstone" - | "m.room.topic" => { - #deserialize_event - } - }, - _ => { - return Err(syn::Error::new( - Span::call_site(), - format!("found unrecognized enum variant `{}`", variant), - )) - } - }; - - Ok(tkns) -} diff --git a/ruma-events-macros/src/content_enum.rs b/ruma-events-macros/src/content_enum.rs index 8bef946607..140c2afe7e 100644 --- a/ruma-events-macros/src/content_enum.rs +++ b/ruma-events-macros/src/content_enum.rs @@ -1,6 +1,6 @@ //! Implementation of the content_enum type macro. -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; use syn::{ parse::{self, Parse, ParseStream}, @@ -14,13 +14,6 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result let attrs = &input.attrs; let ident = &input.name; - let ident_chars = ident.to_string().chars().collect::>(); - let event_enum = match ident_chars.as_slice() { - ['A', 'n', 'y', event @ .., 'C', 'o', 'n', 't', 'e', 'n', 't'] => event, - _ => return Err(syn::Error::new(Span::call_site(), "failure")), - }; - - let event_enum_ident = Ident::new(&event_enum.iter().collect::(), ident.span()); let event_type_str = &input.events; let variants = input.events.iter().map(to_camel_case).collect::>(); @@ -61,16 +54,15 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result } }; - let any_event_variant_impl = if let Some(variant) = any_enum_variant(ident) { - quote! { - impl #ident { - fn any_event_variant(&self) -> fn(#event_enum_ident) -> ::ruma_events::AnyEvent { - ::ruma_events::AnyEvent::#variant + let any_event_variant_impl = quote! { + impl #ident { + fn is_compatible(event_type: &str) -> bool { + match event_type { + #( #event_type_str => true, )* + _ => false, } } } - } else { - TokenStream::new() }; let marker_trait_impls = marker_traits(ident); @@ -86,24 +78,6 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result }) } -fn any_enum_variant(ident: &Ident) -> Option { - match ident.to_string().as_str() { - "AnyStateEventContent" => Some(quote! { - State - }), - "AnyMessageEventContent" => Some(quote! { - Message - }), - "AnyEphemeralRoomEventContent" => Some(quote! { - Ephemeral - }), - "AnyBasicEventContent" => Some(quote! { - Basic - }), - _ => None, - } -} - fn marker_traits(ident: &Ident) -> TokenStream { match ident.to_string().as_str() { "AnyStateEventContent" => quote! { diff --git a/ruma-events-macros/src/lib.rs b/ruma-events-macros/src/lib.rs index 36d57ea9ed..84b4a5e151 100644 --- a/ruma-events-macros/src/lib.rs +++ b/ruma-events-macros/src/lib.rs @@ -11,7 +11,6 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; use self::{ - any_deserialize::expand_any_event_deserialize, content_enum::{expand_content_enum, ContentEnumInput}, event::expand_event, event_content::{ @@ -20,7 +19,6 @@ use self::{ }, }; -mod any_deserialize; mod content_enum; mod event; mod event_content; @@ -83,10 +81,3 @@ pub fn derive_state_event(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand_event(input).unwrap_or_else(|err| err.to_compile_error()).into() } - -/// Generates custom `Deserialize implementation for the any event enums. -#[proc_macro_derive(AnyEventDeserialize)] -pub fn derive_any_event_deserialize(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - expand_any_event_deserialize(input).unwrap_or_else(|err| err.to_compile_error()).into() -} diff --git a/ruma-events/Cargo.toml b/ruma-events/Cargo.toml index 23c504768d..2010fd5052 100644 --- a/ruma-events/Cargo.toml +++ b/ruma-events/Cargo.toml @@ -27,3 +27,8 @@ maplit = "1.0.2" matches = "0.1.8" ruma-identifiers = { version = "0.16.2", path = "../ruma-identifiers", features = ["rand"] } trybuild = "1.0.28" +criterion = "0.3.2" + +[[bench]] +name = "event_deserialize" +harness = false diff --git a/ruma-events/benches/event_deserialize.rs b/ruma-events/benches/event_deserialize.rs new file mode 100644 index 0000000000..880421f0e1 --- /dev/null +++ b/ruma-events/benches/event_deserialize.rs @@ -0,0 +1,130 @@ +// In order to run use either `cargo bench` or to save if you +// want to save the result to compare branches use +// `cargo bench --bench event_deserialize -- --save-baseline test-output` +// Since we are in a workspace the default `cargo bench` still picks up +// the args passed to it to avoid this use the above command. + +use criterion::{criterion_group, criterion_main, Criterion}; +use ruma_events::{ + room::{message::MessageEventContent, power_levels::PowerLevelsEventContent}, + AnyRoomEventStub, EventJson, MessageEvent, StateEventStub, +}; +use serde_json::json; + +fn deserialize_any_event_stub(c: &mut Criterion) { + let json_data = json!({ + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 45, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 45 + } + }); + c.bench_function("deserialize to `AnyRoomEventStub`", |b| { + b.iter(|| { + let _ = serde_json::from_value::>(json_data.clone()) + .unwrap() + .deserialize() + .unwrap(); + }) + }); +} + +fn deserialize_specific_event_stub(c: &mut Criterion) { + let json_data = json!({ + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 45, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 45 + } + }); + c.bench_function("deserialize to `StateEventStub`", |b| { + b.iter(|| { + let _ = serde_json::from_value::>>( + json_data.clone(), + ) + .unwrap() + .deserialize() + .unwrap(); + }) + }); +} + +fn deserialize_message_event(c: &mut Criterion) { + let json_data = json!({ + "type": "m.room.message", + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 10_000, + "room_id": "!testroomid:example.org", + "sender": "@user:example.org", + "content": { + "body": "Hello, World!", + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "formatted_body": "Hello, World!", + } + }); + + c.bench_function("deserialize to `MessageEvent`", |b| { + b.iter(|| { + let _ = serde_json::from_value::>>( + json_data.clone(), + ) + .unwrap() + .deserialize() + .unwrap(); + }) + }); +} + +criterion_group!( + benches, + deserialize_any_event_stub, + deserialize_specific_event_stub, + deserialize_message_event +); + +criterion_main!(benches); diff --git a/ruma-events/src/enums.rs b/ruma-events/src/enums.rs index 499509700e..ef926338d9 100644 --- a/ruma-events/src/enums.rs +++ b/ruma-events/src/enums.rs @@ -1,8 +1,9 @@ -use ruma_events_macros::{event_content_enum, AnyEventDeserialize}; -use serde::{de, Serialize}; -use serde_json::{ - from_value as from_json_value, value::RawValue as RawJsonValue, Value as JsonValue, +use ruma_events_macros::event_content_enum; +use serde::{ + de::{self, Error as _}, + Serialize, }; +use serde_json::{from_value as from_json_value, Value as JsonValue}; use crate::{ event_kinds::{ @@ -125,12 +126,12 @@ pub type AnyToDeviceEvent = ToDeviceEvent; pub enum AnyEvent { /// Any basic event. Basic(AnyBasicEvent), - /// `"m.presence"`, the only non-room event with a `sender` field. - Presence(PresenceEvent), /// Any ephemeral room event. Ephemeral(AnyEphemeralRoomEvent), /// Any message event. Message(AnyMessageEvent), + /// `"m.presence"`, the only non-room event with a `sender` field. + Presence(PresenceEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. @@ -138,7 +139,7 @@ pub enum AnyEvent { } /// Any room event. -#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] +#[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyRoomEvent { /// Any message event. @@ -150,7 +151,7 @@ pub enum AnyRoomEvent { } /// Any room event stub (room event without a `room_id`, as returned in `/sync` responses) -#[derive(Clone, Debug, Serialize, AnyEventDeserialize)] +#[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyRoomEventStub { /// Any message event stub @@ -161,21 +162,81 @@ pub enum AnyRoomEventStub { State(AnyStateEventStub), } +// FIXME `#[serde(untagged)]` deserialization fails for these enums which +// is odd as impl<'de> de::Deserialize<'de> for AnyEvent { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { - use de::Error as _; + let json = JsonValue::deserialize(deserializer)?; + let ev_type: String = util::get_field(&json, "type")?; + match ev_type.as_str() { + "m.room.redaction" => { + Ok(AnyEvent::Redaction(from_json_value(json).map_err(D::Error::custom)?)) + } + "m.presence" => { + Ok(AnyEvent::Presence(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyBasicEventContent::is_compatible(ev_type) => { + Ok(AnyEvent::Basic(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyEphemeralRoomEventContent::is_compatible(ev_type) => { + Ok(AnyEvent::Ephemeral(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyMessageEventContent::is_compatible(ev_type) => { + Ok(AnyEvent::Message(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyStateEventContent::is_compatible(ev_type) => { + Ok(AnyEvent::State(from_json_value(json).map_err(D::Error::custom)?)) + } + _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), + } + } +} + +impl<'de> de::Deserialize<'de> for AnyRoomEvent { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let json = JsonValue::deserialize(deserializer)?; + let ev_type: String = util::get_field(&json, "type")?; + + match ev_type.as_str() { + "m.room.redaction" => { + Ok(AnyRoomEvent::Redaction(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyMessageEventContent::is_compatible(ev_type) => { + Ok(AnyRoomEvent::Message(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyStateEventContent::is_compatible(ev_type) => { + Ok(AnyRoomEvent::State(from_json_value(json).map_err(D::Error::custom)?)) + } + _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), + } + } +} + +impl<'de> de::Deserialize<'de> for AnyRoomEventStub { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { let json = JsonValue::deserialize(deserializer)?; - let raw: Box = util::get_field(&json, "content")?; let ev_type: String = util::get_field(&json, "type")?; - let content: AnyStateEventContent = - ruma_events::EventContent::from_parts(&ev_type, raw).map_err(D::Error::custom)?; match ev_type.as_str() { - "hello" => Ok(AnyEvent::Message(from_json_value(json).map_err(D::Error::custom)?)), + "m.room.redaction" => { + Ok(AnyRoomEventStub::Redaction(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyMessageEventContent::is_compatible(ev_type) => { + Ok(AnyRoomEventStub::Message(from_json_value(json).map_err(D::Error::custom)?)) + } + ev_type if AnyStateEventContent::is_compatible(ev_type) => { + Ok(AnyRoomEventStub::State(from_json_value(json).map_err(D::Error::custom)?)) + } _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), } } diff --git a/ruma-events/tests/any_deserialize_derive.rs b/ruma-events/tests/any_deserialize_derive.rs deleted file mode 100644 index 4df505d7d1..0000000000 --- a/ruma-events/tests/any_deserialize_derive.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[test] -fn ui() { - let t = trybuild::TestCases::new(); - t.pass("tests/ui/09-any-deserialize-sanity-check.rs"); - t.compile_fail("tests/ui/10-invalid-variant.rs"); -} diff --git a/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs b/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs deleted file mode 100644 index a738aae4ba..0000000000 --- a/ruma-events/tests/ui/09-any-deserialize-sanity-check.rs +++ /dev/null @@ -1,10 +0,0 @@ -use ruma_events_macros::AnyEventDeserialize; - -#[derive(Clone, Debug, AnyEventDeserialize)] -pub enum AnyRoomEventStub { - Message(()), - Redaction(()), - StateEvent(()), -} - -fn main() {} diff --git a/ruma-events/tests/ui/10-invalid-variant.rs b/ruma-events/tests/ui/10-invalid-variant.rs deleted file mode 100644 index ab7be3ef5c..0000000000 --- a/ruma-events/tests/ui/10-invalid-variant.rs +++ /dev/null @@ -1,10 +0,0 @@ -use ruma_events_macros::AnyEventDeserialize; - -#[derive(Clone, Debug, AnyEventDeserialize)] -pub enum AnyRoomEventStub { - Message(()), - Redaction(()), - NotAnEvent(()), -} - -fn main() {} diff --git a/ruma-events/tests/ui/10-invalid-variant.stderr b/ruma-events/tests/ui/10-invalid-variant.stderr deleted file mode 100644 index c7a47222f5..0000000000 --- a/ruma-events/tests/ui/10-invalid-variant.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: found unrecognized enum variant `NotAnEvent` - --> $DIR/10-invalid-variant.rs:3:24 - | -3 | #[derive(Clone, Debug, AnyEventDeserialize)] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) From 5ea7ad2a0815e74fdf06ff821b36199eff2991e7 Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 15 Jun 2020 15:50:23 -0400 Subject: [PATCH 4/5] Refactor tests Any*Event enum tests, refactor benchmark file, bench levels of specificity --- ruma-events-macros/src/content_enum.rs | 2 +- ruma-events/benches/event_deserialize.rs | 113 ++++----- ruma-events/src/enums.rs | 6 +- ruma-events/tests/{any_enums.rs => enums.rs} | 231 ++++++++----------- 4 files changed, 149 insertions(+), 203 deletions(-) rename ruma-events/tests/{any_enums.rs => enums.rs} (68%) diff --git a/ruma-events-macros/src/content_enum.rs b/ruma-events-macros/src/content_enum.rs index 140c2afe7e..133f5eea3d 100644 --- a/ruma-events-macros/src/content_enum.rs +++ b/ruma-events-macros/src/content_enum.rs @@ -9,7 +9,7 @@ use syn::{ /// Create a content enum from `ContentEnumInput`. /// -/// The is the internals of the `event_content_enum!` macro. +/// This is the internals of the `event_content_enum!` macro. pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result { let attrs = &input.attrs; let ident = &input.name; diff --git a/ruma-events/benches/event_deserialize.rs b/ruma-events/benches/event_deserialize.rs index 880421f0e1..464345f2cc 100644 --- a/ruma-events/benches/event_deserialize.rs +++ b/ruma-events/benches/event_deserialize.rs @@ -1,18 +1,18 @@ -// In order to run use either `cargo bench` or to save if you -// want to save the result to compare branches use -// `cargo bench --bench event_deserialize -- --save-baseline test-output` -// Since we are in a workspace the default `cargo bench` still picks up -// the args passed to it to avoid this use the above command. +// `cargo bench` works but if you use `cargo bench -- --save-baseline ` +// or pass any other args to it it fails with the error +// `cargo bench unknown option --save-baseline`. +// To pass args to criterion use this form +// `cargo bench --bench -- --save-baseline ` use criterion::{criterion_group, criterion_main, Criterion}; use ruma_events::{ - room::{message::MessageEventContent, power_levels::PowerLevelsEventContent}, - AnyRoomEventStub, EventJson, MessageEvent, StateEventStub, + room::power_levels::PowerLevelsEventContent, AnyEvent, AnyRoomEvent, AnyStateEvent, EventJson, + StateEvent, }; use serde_json::json; -fn deserialize_any_event_stub(c: &mut Criterion) { - let json_data = json!({ +fn power_levels() -> serde_json::Value { + json!({ "content": { "ban": 50, "events": { @@ -35,15 +35,21 @@ fn deserialize_any_event_stub(c: &mut Criterion) { "event_id": "$15139375512JaHAW:localhost", "origin_server_ts": 45, "sender": "@example:localhost", + "room_id": "!room:localhost", "state_key": "", "type": "m.room.power_levels", "unsigned": { "age": 45 } - }); - c.bench_function("deserialize to `AnyRoomEventStub`", |b| { + }) +} + +fn deserialize_any_event(c: &mut Criterion) { + let json_data = power_levels(); + + c.bench_function("deserialize to `AnyEvent`", |b| { b.iter(|| { - let _ = serde_json::from_value::>(json_data.clone()) + let _ = serde_json::from_value::>(json_data.clone()) .unwrap() .deserialize() .unwrap(); @@ -51,66 +57,38 @@ fn deserialize_any_event_stub(c: &mut Criterion) { }); } -fn deserialize_specific_event_stub(c: &mut Criterion) { - let json_data = json!({ - "content": { - "ban": 50, - "events": { - "m.room.avatar": 50, - "m.room.canonical_alias": 50, - "m.room.history_visibility": 100, - "m.room.name": 50, - "m.room.power_levels": 100 - }, - "events_default": 0, - "invite": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - "@example:localhost": 100 - }, - "users_default": 0 - }, - "event_id": "$15139375512JaHAW:localhost", - "origin_server_ts": 45, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.power_levels", - "unsigned": { - "age": 45 - } - }); - c.bench_function("deserialize to `StateEventStub`", |b| { +fn deserialize_any_room_event(c: &mut Criterion) { + let json_data = power_levels(); + + c.bench_function("deserialize to `AnyRoomEvent`", |b| { b.iter(|| { - let _ = serde_json::from_value::>>( - json_data.clone(), - ) - .unwrap() - .deserialize() - .unwrap(); + let _ = serde_json::from_value::>(json_data.clone()) + .unwrap() + .deserialize() + .unwrap(); }) }); } -fn deserialize_message_event(c: &mut Criterion) { - let json_data = json!({ - "type": "m.room.message", - "event_id": "$143273582443PhrSn:example.org", - "origin_server_ts": 10_000, - "room_id": "!testroomid:example.org", - "sender": "@user:example.org", - "content": { - "body": "Hello, World!", - "msgtype": "m.text", - "format": "org.matrix.custom.html", - "formatted_body": "Hello, World!", - } +fn deserialize_any_state_event(c: &mut Criterion) { + let json_data = power_levels(); + + c.bench_function("deserialize to `AnyStateEvent`", |b| { + b.iter(|| { + let _ = serde_json::from_value::>(json_data.clone()) + .unwrap() + .deserialize() + .unwrap(); + }) }); +} + +fn deserialize_specific_event(c: &mut Criterion) { + let json_data = power_levels(); - c.bench_function("deserialize to `MessageEvent`", |b| { + c.bench_function("deserialize to `StateEvent`", |b| { b.iter(|| { - let _ = serde_json::from_value::>>( + let _ = serde_json::from_value::>>( json_data.clone(), ) .unwrap() @@ -122,9 +100,10 @@ fn deserialize_message_event(c: &mut Criterion) { criterion_group!( benches, - deserialize_any_event_stub, - deserialize_specific_event_stub, - deserialize_message_event + deserialize_any_event, + deserialize_any_room_event, + deserialize_any_state_event, + deserialize_specific_event ); criterion_main!(benches); diff --git a/ruma-events/src/enums.rs b/ruma-events/src/enums.rs index ef926338d9..65dba07290 100644 --- a/ruma-events/src/enums.rs +++ b/ruma-events/src/enums.rs @@ -126,12 +126,12 @@ pub type AnyToDeviceEvent = ToDeviceEvent; pub enum AnyEvent { /// Any basic event. Basic(AnyBasicEvent), + /// `"m.presence"`, the only non-room event with a `sender` field. + Presence(PresenceEvent), /// Any ephemeral room event. Ephemeral(AnyEphemeralRoomEvent), /// Any message event. Message(AnyMessageEvent), - /// `"m.presence"`, the only non-room event with a `sender` field. - Presence(PresenceEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. @@ -163,7 +163,7 @@ pub enum AnyRoomEventStub { } // FIXME `#[serde(untagged)]` deserialization fails for these enums which -// is odd as +// is odd as we are doing basically the same thing here, investigate? impl<'de> de::Deserialize<'de> for AnyEvent { fn deserialize(deserializer: D) -> Result where diff --git a/ruma-events/tests/any_enums.rs b/ruma-events/tests/enums.rs similarity index 68% rename from ruma-events/tests/any_enums.rs rename to ruma-events/tests/enums.rs index 71291be96b..733c03bade 100644 --- a/ruma-events/tests/any_enums.rs +++ b/ruma-events/tests/enums.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use matches::assert_matches; use ruma_identifiers::RoomAliasId; -use serde_json::{from_value as from_json_value, json}; +use serde_json::{from_value as from_json_value, json, Value as JsonValue}; use ruma_events::{ room::{ @@ -11,9 +11,79 @@ use ruma_events::{ power_levels::PowerLevelsEventContent, }, AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent, - EventJson, MessageEvent, MessageEventStub, StateEvent, StateEventStub, + MessageEvent, MessageEventStub, StateEvent, StateEventStub, }; +fn message_event() -> JsonValue { + json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "room_id": "!room:room.com", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }) +} + +fn message_event_stub() -> JsonValue { + json!({ + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 1 + } + }) +} + +fn aliases_event() -> JsonValue { + json!({ + "content": { + "aliases": ["#somewhere:localhost"] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "room_id": "!room:room.com", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }) +} + +fn aliases_event_stub() -> JsonValue { + json!({ + "content": { + "aliases": ["#somewhere:localhost"] + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.aliases", + "unsigned": { + "age": 1 + } + }) +} + #[test] fn power_event_stub_deserialization() { let json_data = json!({ @@ -47,46 +117,26 @@ fn power_event_stub_deserialization() { }); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::State( + from_json_value::(json_data), + Ok(AnyRoomEventStub::State( StateEventStub { content: AnyStateEventContent::RoomPowerLevels(PowerLevelsEventContent { ban, .. }), .. } - ) + )) if ban == js_int::Int::new(50).unwrap() ); } #[test] fn message_event_stub_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); + let json_data = message_event_stub(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::Message( + from_json_value::(json_data), + Ok(AnyRoomEventStub::Message( MessageEventStub { content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { body, @@ -95,69 +145,36 @@ fn message_event_stub_deserialization() { })), .. } - ) + )) if body == "baba" && formatted.body == "baba" ); } #[test] fn aliases_event_stub_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); + let json_data = aliases_event_stub(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEventStub::State( + from_json_value::(json_data), + Ok(AnyRoomEventStub::State( StateEventStub { content: AnyStateEventContent::RoomAliases(AliasesEventContent { aliases, }), .. } - ) + )) if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] ); } #[test] fn message_room_event_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "room_id": "!room:room.com", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); + let json_data = message_event(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEvent::Message( + from_json_value::(json_data), + Ok(AnyRoomEvent::Message( MessageEvent { content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { body, @@ -166,70 +183,36 @@ fn message_room_event_deserialization() { })), .. } - ) + )) if body == "baba" && formatted.body == "baba" ); } #[test] fn alias_room_event_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ "#somewhere:localhost" ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "room_id": "!room:room.com", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); + let json_data = aliases_event(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyRoomEvent::State( + from_json_value::(json_data), + Ok(AnyRoomEvent::State( StateEvent { content: AnyStateEventContent::RoomAliases(AliasesEventContent { aliases, }), .. } - ) + )) if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] ); } #[test] fn message_event_deserialization() { - let json_data = json!({ - "content": { - "body": "baba", - "format": "org.matrix.custom.html", - "formatted_body": "baba", - "msgtype": "m.text" - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "room_id": "!room:room.com", - "type": "m.room.message", - "unsigned": { - "age": 1 - } - }); + let json_data = message_event(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyEvent::Message( + from_json_value::(json_data), + Ok(AnyEvent::Message( MessageEvent { content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { body, @@ -238,41 +221,25 @@ fn message_event_deserialization() { })), .. } - ) + )) if body == "baba" && formatted.body == "baba" ); } #[test] fn alias_event_deserialization() { - let json_data = json!({ - "content": { - "aliases": [ "#somewhere:localhost" ] - }, - "event_id": "$152037280074GZeOm:localhost", - "origin_server_ts": 1, - "sender": "@example:localhost", - "state_key": "", - "room_id": "!room:room.com", - "type": "m.room.aliases", - "unsigned": { - "age": 1 - } - }); + let json_data = aliases_event(); assert_matches!( - from_json_value::>(json_data) - .unwrap() - .deserialize() - .unwrap(), - AnyEvent::State( + from_json_value::(json_data), + Ok(AnyEvent::State( StateEvent { content: AnyStateEventContent::RoomAliases(AliasesEventContent { aliases, }), .. } - ) + )) if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] ); } From be801074718009badd6c3211e0950c1899af0165 Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 15 Jun 2020 17:21:25 -0400 Subject: [PATCH 5/5] Fix grammar in benchmark comments --- ruma-events/benches/event_deserialize.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ruma-events/benches/event_deserialize.rs b/ruma-events/benches/event_deserialize.rs index 464345f2cc..47026ade88 100644 --- a/ruma-events/benches/event_deserialize.rs +++ b/ruma-events/benches/event_deserialize.rs @@ -1,8 +1,8 @@ -// `cargo bench` works but if you use `cargo bench -- --save-baseline ` -// or pass any other args to it it fails with the error +// `cargo bench` works, but if you use `cargo bench -- --save-baseline ` +// or pass any other args to it, it fails with the error // `cargo bench unknown option --save-baseline`. -// To pass args to criterion use this form -// `cargo bench --bench -- --save-baseline ` +// To pass args to criterion, use this form +// `cargo bench --bench -- --save-baseline `. use criterion::{criterion_group, criterion_main, Criterion}; use ruma_events::{