Revert "Remove special handling of custom events"

This reverts commit 539dd5f31dff172b5d6559378e33982d0a0c3333.
This commit is contained in:
Jonas Platte 2020-03-24 11:31:36 +01:00
parent 539dd5f31d
commit 5a79b766c7
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
3 changed files with 155 additions and 261 deletions

View File

@ -30,6 +30,9 @@ pub struct RumaEvent {
/// Struct fields of the event. /// Struct fields of the event.
fields: Vec<Field>, fields: Vec<Field>,
/// Whether or not the event type is `EventType::Custom`.
is_custom: bool,
/// The kind of event. /// The kind of event.
kind: EventKind, kind: EventKind,
@ -43,18 +46,24 @@ impl From<RumaEventInput> for RumaEvent {
let name = input.name; let name = input.name;
let content_name = format_ident!("{}Content", name, span = Span::call_site()); let content_name = format_ident!("{}Content", name, span = Span::call_site());
let event_type = input.event_type; let event_type = input.event_type;
let is_custom = is_custom_event_type(&event_type);
let mut fields = match kind { let mut fields = match kind {
EventKind::Event => { EventKind::Event => populate_event_fields(
populate_event_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new)) is_custom,
} content_name.clone(),
EventKind::RoomEvent => populate_room_event_fields( 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(), content_name.clone(),
input.fields.unwrap_or_else(Vec::new), 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()); fields.sort_unstable_by_key(|field| field.ident.clone().unwrap());
@ -65,6 +74,7 @@ impl From<RumaEventInput> for RumaEvent {
content_name, content_name,
event_type, event_type,
fields, fields,
is_custom,
kind, kind,
name, name,
} }
@ -80,8 +90,13 @@ impl ToTokens for RumaEvent {
let content_name = &self.content_name; let content_name = &self.content_name;
let event_fields = &self.fields; 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; let event_type = &self.event_type;
quote! { quote! {
#event_type #event_type
} }
@ -125,9 +140,9 @@ impl ToTokens for RumaEvent {
Content::Typedef(_) => TokenStream::new(), Content::Typedef(_) => TokenStream::new(),
}; };
// Only custom events will already have an event_type field, since we don't handle // Custom events will already have an event_type field. All other events need to account
// custom events we start at one. // for this field being manually inserted in `Serialize` impls.
let mut base_field_count: usize = 1; 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 // 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. // 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); 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<TokenStream> = optional_field_idents let increment_struct_len_statements: Vec<TokenStream> = optional_field_idents
.iter() .iter()
.map(|ident| { .map(|ident| {
@ -289,7 +321,8 @@ impl ToTokens for RumaEvent {
S: serde::Serializer S: serde::Serializer
{ {
use serde::ser::SerializeStruct as _; use serde::ser::SerializeStruct as _;
use ::ruma_events::Event as _;
#import_event_in_serialize_impl
let mut len = #base_field_count; let mut len = #base_field_count;
@ -298,7 +331,7 @@ impl ToTokens for RumaEvent {
let mut state = serializer.serialize_struct(#name_str, len)?; let mut state = serializer.serialize_struct(#name_str, len)?;
#(#serialize_field_calls)* #(#serialize_field_calls)*
state.serialize_field("type", &self.event_type())?; #manually_serialize_type_field
state.end() state.end()
} }
@ -342,10 +375,25 @@ impl ToTokens for RumaEvent {
} }
/// Fills in the event's struct definition with fields common to all basic events. /// Fills in the event's struct definition with fields common to all basic events.
fn populate_event_fields(content_name: Ident, mut fields: Vec<Field>) -> Vec<Field> { fn populate_event_fields(
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! { is_custom: bool,
/// The event's content. content_name: Ident,
pub content: #content_name, mut fields: Vec<Field>,
) -> Vec<Field> {
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = 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)); fields.extend(punctuated_fields.into_iter().map(|p| p.field));
@ -354,8 +402,12 @@ fn populate_event_fields(content_name: Ident, mut fields: Vec<Field>) -> Vec<Fie
} }
/// Fills in the event's struct definition with fields common to all room events. /// Fills in the event's struct definition with fields common to all room events.
fn populate_room_event_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> { fn populate_room_event_fields(
let mut fields = populate_event_fields(content_name, fields); is_custom: bool,
content_name: Ident,
fields: Vec<Field>,
) -> Vec<Field> {
let mut fields = populate_event_fields(is_custom, content_name, fields);
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! { let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
/// The unique identifier for the event. /// The unique identifier for the event.
@ -381,8 +433,8 @@ fn populate_room_event_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Fi
} }
/// Fills in the event's struct definition with fields common to all state events. /// Fills in the event's struct definition with fields common to all state events.
fn populate_state_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> { fn populate_state_fields(is_custom: bool, content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
let mut fields = populate_room_event_fields(content_name.clone(), fields); let mut fields = populate_room_event_fields(is_custom, content_name.clone(), fields);
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! { let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
/// The previous content for this state key, if any. /// The previous content for this state key, if any.
@ -397,6 +449,11 @@ fn populate_state_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field>
fields 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`. /// Checks if a type is an `Option`.
fn is_option(ty: &Type) -> bool { fn is_option(ty: &Type) -> bool {
if let Type::Path(ref type_path) = ty { if let Type::Path(ref type_path) = ty {

View File

@ -406,265 +406,53 @@ pub trait StateEvent: RoomEvent {
fn state_key(&self) -> &str; fn state_key(&self) -> &str;
} }
/// A basic custom event outside of the Matrix specification.
mod custom { mod custom {
use super::{Event, EventType}; use ruma_events_macros::ruma_event;
use ruma_events_macros::FromRaw;
use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
/// A custom event not covered by the Matrix specification. ruma_event! {
#[derive(Clone, Debug, FromRaw, PartialEq, Serialize)] /// A custom basic event not covered by the Matrix specification.
pub struct CustomEvent { CustomEvent {
/// The event's content. kind: Event,
pub content: CustomEventContent, event_type: Custom,
/// The custom type of the event. content_type_alias: {
#[serde(rename = "type")] /// The payload for `CustomEvent`.
pub event_type: String, Value
} },
/// 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 { mod custom_room {
use super::{Event, EventType, RoomEvent}; use ruma_events_macros::ruma_event;
use ruma_events_macros::FromRaw;
use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
/// A custom room event not covered by the Matrix specification. ruma_event! {
#[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<ruma_identifiers::RoomId>,
/// 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<serde_json::Value>,
}
/// 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. /// A custom room event not covered by the Matrix specification.
#[derive(Clone, Debug, PartialEq, Deserialize)] CustomRoomEvent {
pub struct CustomRoomEvent { kind: RoomEvent,
/// The event's content. event_type: Custom,
pub content: CustomRoomEventContent, content_type_alias: {
/// The unique identifier for the event. /// The payload for `CustomRoomEvent`.
pub event_id: ruma_identifiers::EventId, Value
/// 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<ruma_identifiers::RoomId>,
/// 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<serde_json::Value>,
} }
} }
} }
mod custom_state { mod custom_state {
use super::{Event, EventType, RoomEvent, StateEvent}; use ruma_events_macros::ruma_event;
use ruma_events_macros::FromRaw;
use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
/// A custom state event not covered by the Matrix specification. ruma_event! {
#[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<CustomStateEventContent>,
/// The unique identifier for the room associated with this event.
pub room_id: Option<ruma_identifiers::RoomId>,
/// 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<serde_json::Value>,
}
/// 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. /// A custom state event not covered by the Matrix specification.
#[derive(Clone, Debug, PartialEq, Deserialize)] CustomStateEvent {
pub struct CustomStateEvent { kind: StateEvent,
/// The event's content. event_type: Custom,
pub content: CustomStateEventContent, content_type_alias: {
/// The unique identifier for the event. /// The payload for `CustomStateEvent`.
pub event_id: ruma_identifiers::EventId, Value
/// 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<CustomStateEventContent>,
/// The unique identifier for the room associated with this event.
pub room_id: Option<ruma_identifiers::RoomId>,
/// 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<serde_json::Value>,
} }
} }
} }

View File

@ -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::<Value>(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 { mod extra_fields {
use super::*; use super::*;