From 5a79b766c7f6a3c569839d6cfd24518b664e1d0b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 24 Mar 2020 11:31:36 +0100 Subject: [PATCH] Revert "Remove special handling of custom events" This reverts commit 539dd5f31dff172b5d6559378e33982d0a0c3333. --- ruma-events-macros/src/gen.rs | 99 ++++++++++--- src/lib.rs | 268 ++++------------------------------ tests/ruma_events_macros.rs | 49 +++++++ 3 files changed, 155 insertions(+), 261 deletions(-) diff --git a/ruma-events-macros/src/gen.rs b/ruma-events-macros/src/gen.rs index e88b54fb..c365a034 100644 --- a/ruma-events-macros/src/gen.rs +++ b/ruma-events-macros/src/gen.rs @@ -30,6 +30,9 @@ pub struct RumaEvent { /// Struct fields of the event. fields: Vec, + /// Whether or not the event type is `EventType::Custom`. + is_custom: bool, + /// The kind of event. kind: EventKind, @@ -43,18 +46,24 @@ impl From for RumaEvent { let name = input.name; let content_name = format_ident!("{}Content", name, span = Span::call_site()); let event_type = input.event_type; + let is_custom = is_custom_event_type(&event_type); let mut fields = match kind { - EventKind::Event => { - populate_event_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new)) - } - EventKind::RoomEvent => populate_room_event_fields( + EventKind::Event => populate_event_fields( + is_custom, + content_name.clone(), + input.fields.unwrap_or_else(Vec::new), + ), + EventKind::RoomEvent => populate_room_event_fields( + is_custom, + content_name.clone(), + input.fields.unwrap_or_else(Vec::new), + ), + EventKind::StateEvent => populate_state_fields( + is_custom, content_name.clone(), input.fields.unwrap_or_else(Vec::new), ), - EventKind::StateEvent => { - populate_state_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new)) - } }; fields.sort_unstable_by_key(|field| field.ident.clone().unwrap()); @@ -65,6 +74,7 @@ impl From for RumaEvent { content_name, event_type, fields, + is_custom, kind, name, } @@ -80,8 +90,13 @@ impl ToTokens for RumaEvent { let content_name = &self.content_name; let event_fields = &self.fields; - let event_type = { + let event_type = if self.is_custom { + quote! { + ::ruma_events::EventType::Custom(self.event_type.clone()) + } + } else { let event_type = &self.event_type; + quote! { #event_type } @@ -125,9 +140,9 @@ 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; + // 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 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. @@ -166,6 +181,23 @@ impl ToTokens for RumaEvent { serialize_field_calls.push(serialize_field_call); } + let (manually_serialize_type_field, import_event_in_serialize_impl) = if self.is_custom { + (TokenStream::new(), TokenStream::new()) + } else { + let manually_serialize_type_field = quote! { + state.serialize_field("type", &self.event_type())?; + }; + + let import_event_in_serialize_impl = quote! { + use ::ruma_events::Event as _; + }; + + ( + manually_serialize_type_field, + import_event_in_serialize_impl, + ) + }; + let increment_struct_len_statements: Vec = optional_field_idents .iter() .map(|ident| { @@ -289,7 +321,8 @@ impl ToTokens for RumaEvent { S: serde::Serializer { use serde::ser::SerializeStruct as _; - use ::ruma_events::Event as _; + + #import_event_in_serialize_impl let mut len = #base_field_count; @@ -298,7 +331,7 @@ impl ToTokens for RumaEvent { let mut state = serializer.serialize_struct(#name_str, len)?; #(#serialize_field_calls)* - state.serialize_field("type", &self.event_type())?; + #manually_serialize_type_field state.end() } @@ -342,10 +375,25 @@ impl ToTokens for RumaEvent { } /// Fills in the event's struct definition with fields common to all basic events. -fn populate_event_fields(content_name: Ident, mut fields: Vec) -> Vec { - let punctuated_fields: Punctuated = parse_quote! { - /// The event's content. - pub content: #content_name, +fn populate_event_fields( + is_custom: bool, + content_name: Ident, + mut fields: Vec, +) -> Vec { + let punctuated_fields: Punctuated = if is_custom { + parse_quote! { + /// The event's content. + pub content: #content_name, + + /// The custom type of the event. + #[serde(rename = "type")] + pub event_type: String, + } + } else { + parse_quote! { + /// The event's content. + pub content: #content_name, + } }; fields.extend(punctuated_fields.into_iter().map(|p| p.field)); @@ -354,8 +402,12 @@ fn populate_event_fields(content_name: Ident, mut fields: Vec) -> Vec) -> Vec { - let mut fields = populate_event_fields(content_name, fields); +fn populate_room_event_fields( + is_custom: bool, + content_name: Ident, + fields: Vec, +) -> Vec { + let mut fields = populate_event_fields(is_custom, content_name, fields); let punctuated_fields: Punctuated = parse_quote! { /// The unique identifier for the event. @@ -381,8 +433,8 @@ fn populate_room_event_fields(content_name: Ident, fields: Vec) -> Vec) -> Vec { - let mut fields = populate_room_event_fields(content_name.clone(), fields); +fn populate_state_fields(is_custom: bool, content_name: Ident, fields: Vec) -> Vec { + let mut fields = populate_room_event_fields(is_custom, content_name.clone(), fields); let punctuated_fields: Punctuated = parse_quote! { /// The previous content for this state key, if any. @@ -397,6 +449,11 @@ fn populate_state_fields(content_name: Ident, fields: Vec) -> Vec fields } +/// Checks if the given `Path` refers to `EventType::Custom`. +fn is_custom_event_type(event_type: &Path) -> bool { + event_type.segments.last().unwrap().ident == "Custom" +} + /// Checks if a type is an `Option`. fn is_option(ty: &Type) -> bool { if let Type::Path(ref type_path) = ty { diff --git a/src/lib.rs b/src/lib.rs index 9a4b6e5a..efc0722e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -406,265 +406,53 @@ pub trait StateEvent: RoomEvent { fn state_key(&self) -> &str; } -/// A basic custom event outside of the Matrix specification. mod custom { - use super::{Event, EventType}; - - use ruma_events_macros::FromRaw; - use serde::{Deserialize, Serialize}; + use ruma_events_macros::ruma_event; use serde_json::Value; - /// A custom event not covered by the Matrix specification. - #[derive(Clone, Debug, FromRaw, PartialEq, Serialize)] - pub struct CustomEvent { - /// The event's content. - pub content: CustomEventContent, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, - } - - /// The payload for `CustomEvent`. - pub type CustomEventContent = Value; - - impl Event for CustomEvent { - /// The type of this event's `content` field. - type Content = CustomEventContent; - /// The event's content. - fn content(&self) -> &Self::Content { - &self.content - } - /// The type of the event. - fn event_type(&self) -> EventType { - EventType::Custom(self.event_type.clone()) - } - } - - /// "Raw" versions of the event and its content which implement `serde::Deserialize`. - pub mod raw { - use super::*; - - /// A custom event not covered by the Matrix specification. - #[derive(Clone, Debug, PartialEq, Deserialize)] - pub struct CustomEvent { - /// The event's content. - pub content: CustomEventContent, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, + ruma_event! { + /// A custom basic event not covered by the Matrix specification. + CustomEvent { + kind: Event, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomEvent`. + Value + }, } } } mod custom_room { - use super::{Event, EventType, RoomEvent}; - - use ruma_events_macros::FromRaw; - use serde::{Deserialize, Serialize}; + use ruma_events_macros::ruma_event; use serde_json::Value; - /// A custom room event not covered by the Matrix specification. - #[derive(Clone, Debug, FromRaw, PartialEq, Serialize)] - pub struct CustomRoomEvent { - /// The event's content. - pub content: CustomRoomEventContent, - /// The unique identifier for the event. - pub event_id: ruma_identifiers::EventId, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this - /// event was sent. - pub origin_server_ts: js_int::UInt, - /// The unique identifier for the room associated with this event. - pub room_id: Option, - /// The unique identifier for the user who sent this event. - pub sender: ruma_identifiers::UserId, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Option, - } - - /// The payload for `CustomRoomEvent`. - pub type CustomRoomEventContent = Value; - - impl Event for CustomRoomEvent { - /// The type of this event's `content` field. - type Content = CustomRoomEventContent; - /// The event's content. - fn content(&self) -> &Self::Content { - &self.content - } - /// The type of the event. - fn event_type(&self) -> EventType { - EventType::Custom(self.event_type.clone()) - } - } - - impl RoomEvent for CustomRoomEvent { - /// The unique identifier for the event. - fn event_id(&self) -> &ruma_identifiers::EventId { - &self.event_id - } - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was - /// sent. - fn origin_server_ts(&self) -> js_int::UInt { - self.origin_server_ts - } - /// The unique identifier for the room associated with this event. - /// - /// This can be `None` if the event came from a context where there is - /// no ambiguity which room it belongs to, like a `/sync` response for example. - fn room_id(&self) -> Option<&ruma_identifiers::RoomId> { - self.room_id.as_ref() - } - /// The unique identifier for the user who sent this event. - fn sender(&self) -> &ruma_identifiers::UserId { - &self.sender - } - /// Additional key-value pairs not signed by the homeserver. - fn unsigned(&self) -> Option<&serde_json::Value> { - self.unsigned.as_ref() - } - } - - pub mod raw { - use super::*; - + ruma_event! { /// A custom room event not covered by the Matrix specification. - #[derive(Clone, Debug, PartialEq, Deserialize)] - pub struct CustomRoomEvent { - /// The event's content. - pub content: CustomRoomEventContent, - /// The unique identifier for the event. - pub event_id: ruma_identifiers::EventId, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this - /// event was sent. - pub origin_server_ts: js_int::UInt, - /// The unique identifier for the room associated with this event. - pub room_id: Option, - /// The unique identifier for the user who sent this event. - pub sender: ruma_identifiers::UserId, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Option, + CustomRoomEvent { + kind: RoomEvent, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomRoomEvent`. + Value + }, } } } mod custom_state { - use super::{Event, EventType, RoomEvent, StateEvent}; - - use ruma_events_macros::FromRaw; - use serde::{Deserialize, Serialize}; + use ruma_events_macros::ruma_event; use serde_json::Value; - /// A custom state event not covered by the Matrix specification. - #[derive(Clone, Debug, FromRaw, PartialEq, Serialize)] - pub struct CustomStateEvent { - /// The event's content. - pub content: CustomStateEventContent, - /// The unique identifier for the event. - pub event_id: ruma_identifiers::EventId, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this - /// event was sent. - pub origin_server_ts: js_int::UInt, - /// The previous content for this state key, if any. - pub prev_content: Option, - /// The unique identifier for the room associated with this event. - pub room_id: Option, - /// The unique identifier for the user who sent this event. - pub sender: ruma_identifiers::UserId, - /// A key that determines which piece of room state the event represents. - pub state_key: String, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Option, - } - - /// The payload for `CustomStateEvent`. - pub type CustomStateEventContent = Value; - - impl Event for CustomStateEvent { - /// The type of this event's `content` field. - type Content = CustomStateEventContent; - /// The event's content. - fn content(&self) -> &Self::Content { - &self.content - } - /// The type of the event. - fn event_type(&self) -> EventType { - EventType::Custom(self.event_type.clone()) - } - } - - impl RoomEvent for CustomStateEvent { - /// The unique identifier for the event. - fn event_id(&self) -> &ruma_identifiers::EventId { - &self.event_id - } - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was - /// sent. - fn origin_server_ts(&self) -> js_int::UInt { - self.origin_server_ts - } - /// The unique identifier for the room associated with this event. - /// - /// This can be `None` if the event came from a context where there is - /// no ambiguity which room it belongs to, like a `/sync` response for example. - fn room_id(&self) -> Option<&ruma_identifiers::RoomId> { - self.room_id.as_ref() - } - /// The unique identifier for the user who sent this event. - fn sender(&self) -> &ruma_identifiers::UserId { - &self.sender - } - /// Additional key-value pairs not signed by the homeserver. - fn unsigned(&self) -> Option<&serde_json::Value> { - self.unsigned.as_ref() - } - } - - impl StateEvent for CustomStateEvent { - /// The previous content for this state key, if any. - fn prev_content(&self) -> Option<&Self::Content> { - self.prev_content.as_ref() - } - /// A key that determines which piece of room state the event represents. - fn state_key(&self) -> &str { - &self.state_key - } - } - - pub mod raw { - use super::*; - + ruma_event! { /// A custom state event not covered by the Matrix specification. - #[derive(Clone, Debug, PartialEq, Deserialize)] - pub struct CustomStateEvent { - /// The event's content. - pub content: CustomStateEventContent, - /// The unique identifier for the event. - pub event_id: ruma_identifiers::EventId, - /// The custom type of the event. - #[serde(rename = "type")] - pub event_type: String, - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this - /// event was sent. - pub origin_server_ts: js_int::UInt, - /// The previous content for this state key, if any. - pub prev_content: Option, - /// The unique identifier for the room associated with this event. - pub room_id: Option, - /// The unique identifier for the user who sent this event. - pub sender: ruma_identifiers::UserId, - /// A key that determines which piece of room state the event represents. - pub state_key: String, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Option, + CustomStateEvent { + kind: StateEvent, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomStateEvent`. + Value + }, } } } diff --git a/tests/ruma_events_macros.rs b/tests/ruma_events_macros.rs index 7bc2fc84..b5b95645 100644 --- a/tests/ruma_events_macros.rs +++ b/tests/ruma_events_macros.rs @@ -119,6 +119,55 @@ mod common_case { } } +mod custom_event_type { + use super::*; + + ruma_event! { + /// A custom event. + CustomEvent { + kind: Event, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomEvent`. + Value + }, + } + } + + #[test] + fn value_is_not_null() { + // Hint: serde_json::Value with default feature is sort + // alphabetically rather than preserve the sequence of json kv + // pairs. Check: + // + https://github.com/serde-rs/json/pull/80 + // + https://github.com/serde-rs/json/blob/17d9a5ea9b8e11f01b0fcf13933c4a12d3f8db45/tests/map.rs. + let event = CustomEvent { + content: { serde_json::from_str::(r#"{"alice":["foo", "bar"]}"#).unwrap() }, + event_type: "foo.bar".to_owned(), + }; + let json = json!({ + "content": { + "alice": ["foo", "bar"] + }, + "type": "foo.bar" + }); + serde_json_eq_try_from_raw(event, json); + } + + #[test] + fn value_is_null() { + let event = CustomEvent { + content: { Value::Null }, + event_type: "foo.bar".to_owned(), + }; + let json = json!({ + "content": null, + "type": "foo.bar" + }); + serde_json_eq_try_from_raw(event, json); + } +} + mod extra_fields { use super::*;