diff --git a/ruma-events-macros/src/gen.rs b/ruma-events-macros/src/gen.rs index c365a034..e88b54fb 100644 --- a/ruma-events-macros/src/gen.rs +++ b/ruma-events-macros/src/gen.rs @@ -30,9 +30,6 @@ 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, @@ -46,24 +43,18 @@ 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( - is_custom, - content_name.clone(), - input.fields.unwrap_or_else(Vec::new), - ), + EventKind::Event => { + populate_event_fields(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()); @@ -74,7 +65,6 @@ impl From for RumaEvent { content_name, event_type, fields, - is_custom, kind, name, } @@ -90,13 +80,8 @@ impl ToTokens for RumaEvent { let content_name = &self.content_name; let event_fields = &self.fields; - let event_type = if self.is_custom { - quote! { - ::ruma_events::EventType::Custom(self.event_type.clone()) - } - } else { + let event_type = { let event_type = &self.event_type; - quote! { #event_type } @@ -140,9 +125,9 @@ impl ToTokens for RumaEvent { Content::Typedef(_) => TokenStream::new(), }; - // 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 }; + // 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. @@ -181,23 +166,6 @@ 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| { @@ -321,8 +289,7 @@ impl ToTokens for RumaEvent { S: serde::Serializer { use serde::ser::SerializeStruct as _; - - #import_event_in_serialize_impl + use ::ruma_events::Event as _; let mut len = #base_field_count; @@ -331,7 +298,7 @@ impl ToTokens for RumaEvent { let mut state = serializer.serialize_struct(#name_str, len)?; #(#serialize_field_calls)* - #manually_serialize_type_field + state.serialize_field("type", &self.event_type())?; state.end() } @@ -375,25 +342,10 @@ impl ToTokens for RumaEvent { } /// Fills in the event's struct definition with fields common to all basic events. -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, - } +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, }; fields.extend(punctuated_fields.into_iter().map(|p| p.field)); @@ -402,12 +354,8 @@ fn populate_event_fields( } /// Fills in the event's struct definition with fields common to all room events. -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); +fn populate_room_event_fields(content_name: Ident, fields: Vec) -> Vec { + let mut fields = populate_event_fields(content_name, fields); let punctuated_fields: Punctuated = parse_quote! { /// The unique identifier for the event. @@ -433,8 +381,8 @@ fn populate_room_event_fields( } /// Fills in the event's struct definition with fields common to all state events. -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); +fn populate_state_fields(content_name: Ident, fields: Vec) -> Vec { + let mut fields = populate_room_event_fields(content_name.clone(), fields); let punctuated_fields: Punctuated = parse_quote! { /// The previous content for this state key, if any. @@ -449,11 +397,6 @@ fn populate_state_fields(is_custom: bool, content_name: Ident, fields: Vec 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 efc0722e..9a4b6e5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -406,53 +406,265 @@ pub trait StateEvent: RoomEvent { fn state_key(&self) -> &str; } +/// A basic custom event outside of the Matrix specification. mod custom { - use ruma_events_macros::ruma_event; + use super::{Event, EventType}; + + use ruma_events_macros::FromRaw; + use serde::{Deserialize, Serialize}; use serde_json::Value; - 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 - }, + /// 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, } } } mod custom_room { - use ruma_events_macros::ruma_event; + use super::{Event, EventType, RoomEvent}; + + use ruma_events_macros::FromRaw; + use serde::{Deserialize, Serialize}; use serde_json::Value; - ruma_event! { + /// 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::*; + /// A custom room event not covered by the Matrix specification. - CustomRoomEvent { - kind: RoomEvent, - event_type: Custom, - content_type_alias: { - /// The payload for `CustomRoomEvent`. - Value - }, + #[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, } } } mod custom_state { - use ruma_events_macros::ruma_event; + use super::{Event, EventType, RoomEvent, StateEvent}; + + use ruma_events_macros::FromRaw; + use serde::{Deserialize, Serialize}; use serde_json::Value; - ruma_event! { + /// 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::*; + /// A custom state event not covered by the Matrix specification. - CustomStateEvent { - kind: StateEvent, - event_type: Custom, - content_type_alias: { - /// The payload for `CustomStateEvent`. - Value - }, + #[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, } } } diff --git a/tests/ruma_events_macros.rs b/tests/ruma_events_macros.rs index b5b95645..7bc2fc84 100644 --- a/tests/ruma_events_macros.rs +++ b/tests/ruma_events_macros.rs @@ -119,55 +119,6 @@ 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::*;