Generate stub/stripped Any*Event enums in event_enum! macro

* Conditionally emit tokens for content enum in event_enum! and the path
  for each Any*Event variant contents.
* Add tests for redaction events now that they are part of Any*Event enums.
* Fix any tests that used Any*EventContent.
This commit is contained in:
Ragotzy.devin 2020-06-23 16:33:37 -04:00 committed by GitHub
parent 75ea42961f
commit fdf87a38a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 333 additions and 153 deletions

View File

@ -9,71 +9,56 @@ use syn::{
/// Create a content enum from `EventEnumInput`. /// Create a content enum from `EventEnumInput`.
pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> { pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
let attrs = &input.attrs;
let ident = &input.name; let ident = &input.name;
let event_type_str = &input.events;
let event_struct = Ident::new(&ident.to_string().trim_start_matches("Any"), ident.span());
let variants = input.events.iter().map(to_camel_case).collect::<syn::Result<Vec<_>>>()?; let event_enum = expand_any_enum_with_deserialize(&input, ident)?;
let content = input.events.iter().map(to_event_path).collect::<Vec<_>>();
let event_enum = quote! { let needs_event_content = ident == "AnyStateEvent"
#( #attrs )* || ident == "AnyMessageEvent"
#[derive(Clone, Debug, ::serde::Serialize)] || ident == "AnyToDeviceEvent"
#[serde(untagged)] || ident == "AnyEphemeralRoomEvent"
#[allow(clippy::large_enum_variant)] || ident == "AnyBasicEvent";
pub enum #ident {
#(
#[doc = #event_type_str]
#variants(#content),
)*
/// An event not defined by the Matrix specification
Custom(::ruma_events::#event_struct<::ruma_events::custom::CustomEventContent>),
}
};
let event_deserialize_impl = quote! { let needs_event_stub = ident == "AnyStateEvent" || ident == "AnyMessageEvent";
impl<'de> ::serde::de::Deserialize<'de> for #ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
use ::serde::de::Error as _;
let json = Box::<::serde_json::value::RawValue>::deserialize(deserializer)?; let needs_stripped_event = ident == "AnyStateEvent";
let ::ruma_events::EventDeHelper { ev_type } = ::ruma_events::from_raw_json_value(&json)?;
match ev_type.as_str() {
#(
#event_type_str => {
let event = ::serde_json::from_str::<#content>(json.get()).map_err(D::Error::custom)?;
Ok(#ident::#variants(event))
},
)*
event => {
let event =
::serde_json::from_str::<::ruma_events::#event_struct<::ruma_events::custom::CustomEventContent>>(json.get())
.map_err(D::Error::custom)?;
Ok(Self::Custom(event)) let event_stub_enum =
}, if needs_event_stub { expand_stub_enum(&input)? } else { TokenStream::new() };
}
}
}
};
let event_content_enum = expand_content_enum(input)?; let event_stripped_enum =
if needs_stripped_event { expand_stripped_enum(&input)? } else { TokenStream::new() };
let event_content_enum =
if needs_event_content { expand_content_enum(&input)? } else { TokenStream::new() };
Ok(quote! { Ok(quote! {
#event_enum #event_enum
#event_deserialize_impl #event_stub_enum
#event_stripped_enum
#event_content_enum #event_content_enum
}) })
} }
/// Create a "stub" enum from `EventEnumInput`.
pub fn expand_stub_enum(input: &EventEnumInput) -> syn::Result<TokenStream> {
let ident = Ident::new(&format!("{}Stub", input.name.to_string()), input.name.span());
expand_any_enum_with_deserialize(input, &ident)
}
/// Create a "stripped" enum from `EventEnumInput`.
pub fn expand_stripped_enum(input: &EventEnumInput) -> syn::Result<TokenStream> {
let ident = Ident::new("AnyStrippedStateEventStub", input.name.span());
expand_any_enum_with_deserialize(input, &ident)
}
/// Create a content enum from `EventEnumInput`. /// Create a content enum from `EventEnumInput`.
pub fn expand_content_enum(input: EventEnumInput) -> syn::Result<TokenStream> { pub fn expand_content_enum(input: &EventEnumInput) -> syn::Result<TokenStream> {
let attrs = &input.attrs; let attrs = &input.attrs;
let ident = Ident::new(&format!("{}Content", input.name.to_string()), input.name.span()); let ident = Ident::new(&format!("{}Content", input.name.to_string()), input.name.span());
let event_type_str = &input.events; let event_type_str = &input.events;
@ -122,30 +107,80 @@ pub fn expand_content_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
} }
}; };
let any_event_variant_impl = quote! {
impl #ident {
fn is_compatible(event_type: &str) -> bool {
match event_type {
#( #event_type_str => true, )*
_ => false,
}
}
}
};
let marker_trait_impls = marker_traits(&ident); let marker_trait_impls = marker_traits(&ident);
Ok(quote! { Ok(quote! {
#content_enum #content_enum
#any_event_variant_impl
#event_content_impl #event_content_impl
#marker_trait_impls #marker_trait_impls
}) })
} }
fn expand_any_enum_with_deserialize(
input: &EventEnumInput,
ident: &Ident,
) -> syn::Result<TokenStream> {
let attrs = &input.attrs;
let event_type_str = &input.events;
let event_struct = Ident::new(&ident.to_string().trim_start_matches("Any"), ident.span());
let variants = input.events.iter().map(to_camel_case).collect::<syn::Result<Vec<_>>>()?;
let content =
input.events.iter().map(|event| to_event_path(event, &event_struct)).collect::<Vec<_>>();
let any_enum = quote! {
#( #attrs )*
#[derive(Clone, Debug, ::serde::Serialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum #ident {
#(
#[doc = #event_type_str]
#variants(#content),
)*
/// An event not defined by the Matrix specification
Custom(::ruma_events::#event_struct<::ruma_events::custom::CustomEventContent>),
}
};
let event_deserialize_impl = quote! {
impl<'de> ::serde::de::Deserialize<'de> for #ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
use ::serde::de::Error as _;
let json = Box::<::serde_json::value::RawValue>::deserialize(deserializer)?;
let ::ruma_events::EventDeHelper { ev_type, .. } = ::ruma_events::from_raw_json_value(&json)?;
match ev_type.as_str() {
#(
#event_type_str => {
let event = ::serde_json::from_str::<#content>(json.get()).map_err(D::Error::custom)?;
Ok(#ident::#variants(event))
},
)*
event => {
let event =
::serde_json::from_str::<::ruma_events::#event_struct<::ruma_events::custom::CustomEventContent>>(json.get())
.map_err(D::Error::custom)?;
Ok(Self::Custom(event))
},
}
}
}
};
Ok(quote! {
#any_enum
#event_deserialize_impl
})
}
fn marker_traits(ident: &Ident) -> TokenStream { fn marker_traits(ident: &Ident) -> TokenStream {
match ident.to_string().as_str() { match ident.to_string().as_str() {
"AnyStateEventContent" => quote! { "AnyStateEventContent" => quote! {
@ -166,7 +201,7 @@ fn marker_traits(ident: &Ident) -> TokenStream {
} }
} }
fn to_event_path(name: &LitStr) -> TokenStream { fn to_event_path(name: &LitStr, struct_name: &Ident) -> TokenStream {
let span = name.span(); let span = name.span();
let name = name.value(); let name = name.value();
@ -180,10 +215,25 @@ fn to_event_path(name: &LitStr) -> TokenStream {
.map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..]) .map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
.collect::<String>(); .collect::<String>();
let content_str = Ident::new(&format!("{}Event", event), span);
let path = path.iter().map(|s| Ident::new(s, span)); let path = path.iter().map(|s| Ident::new(s, span));
quote! {
::ruma_events::#( #path )::*::#content_str match struct_name.to_string().as_str() {
"MessageEvent" | "MessageEventStub" if *event_str == "m.room.redaction" => {
let redaction = if struct_name == "MessageEvent" {
quote! { RedactionEvent }
} else {
quote! { RedactionEventStub }
};
quote! { ::ruma_events::room::redaction::#redaction }
}
"ToDeviceEvent" | "StateEventStub" | "StrippedStateEventStub" | "MessageEventStub" => {
let content = Ident::new(&format!("{}EventContent", event), span);
quote! { ::ruma_events::#struct_name<::ruma_events::#( #path )::*::#content> }
}
_ => {
let event_name = Ident::new(&format!("{}Event", event), span);
quote! { ::ruma_events::#( #path )::*::#event_name }
}
} }
} }
@ -253,18 +303,19 @@ pub struct EventEnumInput {
impl Parse for EventEnumInput { impl Parse for EventEnumInput {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> { fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?; let attrs = input.call(Attribute::parse_outer)?;
// name field // "name" field
input.parse::<kw::name>()?; input.parse::<kw::name>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
// the name of our event enum // the name of our event enum
let name: Ident = input.parse()?; let name: Ident = input.parse()?;
input.parse::<Token![,]>()?; input.parse::<Token![,]>()?;
// events field // "events" field
input.parse::<kw::events>()?; input.parse::<kw::events>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
// an array of event names `["m.room.whatever"]` // an array of event names `["m.room.whatever", ...]`
let ev_array = input.parse::<syn::ExprArray>()?; let ev_array = input.parse::<syn::ExprArray>()?;
let events = ev_array let events = ev_array
.elems .elems

View File

@ -1,14 +1,8 @@
use ruma_events_macros::event_enum; use ruma_events_macros::event_enum;
use serde::{ use serde::{de, Serialize};
de::{self, Error},
Serialize,
};
use serde_json::value::RawValue as RawJsonValue; use serde_json::value::RawValue as RawJsonValue;
use crate::{ use crate::{from_raw_json_value, EventDeHelper};
event_kinds::{MessageEventStub, StateEventStub, StrippedStateEventStub},
from_raw_json_value, EventDeHelper,
};
event_enum! { event_enum! {
/// Any basic event. /// Any basic event.
@ -46,7 +40,6 @@ event_enum! {
"m.room.message.feedback", "m.room.message.feedback",
"m.room.redaction", "m.room.redaction",
"m.sticker", "m.sticker",
] ]
} }
@ -66,7 +59,6 @@ event_enum! {
"m.room.name", "m.room.name",
"m.room.pinned_events", "m.room.pinned_events",
"m.room.power_levels", "m.room.power_levels",
"m.room.redaction",
"m.room.server_acl", "m.room.server_acl",
"m.room.third_party_invite", "m.room.third_party_invite",
"m.room.tombstone", "m.room.tombstone",
@ -92,16 +84,6 @@ event_enum! {
] ]
} }
/// Any message event stub (message event without a `room_id`, as returned in `/sync` responses)
pub type AnyMessageEventStub = MessageEventStub<AnyMessageEventContent>;
/// Any state event stub (state event without a `room_id`, as returned in `/sync` responses)
pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>;
/// Any stripped state event stub (stripped-down state event, as returned for rooms the user has
/// been invited to in `/sync` responses)
pub type AnyStrippedStateEventStub = StrippedStateEventStub<AnyStateEventContent>;
/// Any event. /// Any event.
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]
@ -144,22 +126,18 @@ impl<'de> de::Deserialize<'de> for AnyEvent {
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
let json = Box::<RawJsonValue>::deserialize(deserializer)?; let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let EventDeHelper { ev_type } = from_raw_json_value(&json)?; let EventDeHelper { state_key, event_id, room_id, .. } = from_raw_json_value(&json)?;
match ev_type.as_str() { // Determine whether the event is a state, message, ephemeral, or basic event
ev_type if AnyBasicEventContent::is_compatible(ev_type) => { // based on the fields present.
Ok(AnyEvent::Basic(from_raw_json_value(&json)?)) if state_key.is_some() {
} Ok(AnyEvent::State(from_raw_json_value(&json)?))
ev_type if AnyEphemeralRoomEventContent::is_compatible(ev_type) => { } else if event_id.is_some() {
Ok(AnyEvent::Ephemeral(from_raw_json_value(&json)?)) Ok(AnyEvent::Message(from_raw_json_value(&json)?))
} } else if room_id.is_some() {
ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyEvent::Ephemeral(from_raw_json_value(&json)?))
Ok(AnyEvent::Message(from_raw_json_value(&json)?)) } else {
} Ok(AnyEvent::Basic(from_raw_json_value(&json)?))
ev_type if AnyStateEventContent::is_compatible(ev_type) => {
Ok(AnyEvent::State(from_raw_json_value(&json)?))
}
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))),
} }
} }
} }
@ -170,16 +148,12 @@ impl<'de> de::Deserialize<'de> for AnyRoomEvent {
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
let json = Box::<RawJsonValue>::deserialize(deserializer)?; let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let EventDeHelper { ev_type } = from_raw_json_value(&json)?; let EventDeHelper { state_key, .. } = from_raw_json_value(&json)?;
match ev_type.as_str() { if state_key.is_some() {
ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyRoomEvent::State(from_raw_json_value(&json)?))
Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?)) } else {
} Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?))
ev_type if AnyStateEventContent::is_compatible(ev_type) => {
Ok(AnyRoomEvent::State(from_raw_json_value(&json)?))
}
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))),
} }
} }
} }
@ -190,16 +164,12 @@ impl<'de> de::Deserialize<'de> for AnyRoomEventStub {
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
let json = Box::<RawJsonValue>::deserialize(deserializer)?; let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let EventDeHelper { ev_type } = from_raw_json_value(&json)?; let EventDeHelper { state_key, .. } = from_raw_json_value(&json)?;
match ev_type.as_str() { if state_key.is_some() {
ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyRoomEventStub::State(from_raw_json_value(&json)?))
Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?)) } else {
} Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?))
ev_type if AnyStateEventContent::is_compatible(ev_type) => {
Ok(AnyRoomEventStub::State(from_raw_json_value(&json)?))
}
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))),
} }
} }
} }

View File

@ -120,7 +120,10 @@
use std::fmt::Debug; use std::fmt::Debug;
use js_int::Int; use js_int::Int;
use serde::{de, Deserialize, Serialize}; use serde::{
de::{self, IgnoredAny},
Deserialize, Serialize,
};
use serde_json::value::RawValue as RawJsonValue; use serde_json::value::RawValue as RawJsonValue;
use self::room::redaction::RedactionEvent; use self::room::redaction::RedactionEvent;
@ -234,12 +237,24 @@ pub trait MessageEventContent: RoomEventContent {}
/// Marker trait for the content of a state event. /// Marker trait for the content of a state event.
pub trait StateEventContent: RoomEventContent {} pub trait StateEventContent: RoomEventContent {}
/// Helper struct to obtain the event type from a serde_json::value::RawValue. /// Helper struct to determine the event kind from a serde_json::value::RawValue.
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct EventDeHelper { pub struct EventDeHelper {
/// the Matrix event type string "m.room.whatever".
#[serde(rename = "type")] #[serde(rename = "type")]
pub ev_type: String, pub ev_type: String,
/// If `state_key` is present the event will be deserialized as a state event.
pub state_key: Option<IgnoredAny>,
/// If no `state_key` is found but an `event_id` is present the event
/// will be deserialized as a message event.
pub event_id: Option<IgnoredAny>,
/// If no `event_id` or `state_key` are found but a `room_id` is present
/// the event will be deserialized as a ephemeral event.
pub room_id: Option<IgnoredAny>,
} }
/// Helper function for serde_json::value::RawValue deserialization. /// Helper function for serde_json::value::RawValue deserialization.

View File

@ -64,6 +64,10 @@ pub struct RedactionEventContent {
pub reason: Option<String>, pub reason: Option<String>,
} }
impl ruma_events::RoomEventContent for RedactionEventContent {}
impl ruma_events::MessageEventContent for RedactionEventContent {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{ use std::{

View File

@ -5,8 +5,9 @@ use std::{
use matches::assert_matches; use matches::assert_matches;
use ruma_events::{ use ruma_events::{
custom::CustomEventContent, AnyMessageEvent, AnyStateEvent, AnyStateEventContent, EventJson, custom::CustomEventContent, AnyMessageEvent, AnyMessageEventStub, AnyRoomEventStub,
MessageEvent, StateEvent, StateEventStub, UnsignedData, AnyStateEvent, AnyStateEventContent, EventJson, MessageEvent, MessageEventStub, StateEvent,
StateEventStub, UnsignedData,
}; };
use ruma_identifiers::{EventId, RoomId, UserId}; use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::{ use serde_json::{
@ -188,3 +189,50 @@ fn deserialize_custom_state_stub_event() {
&& !unsigned.is_empty() && !unsigned.is_empty()
); );
} }
#[test]
fn deserialize_custom_message_stub_event() {
let json_data = json!({
"content": {
"m.relates_to": {
"event_id": "$MDitXXXXXX",
"key": "👍",
"rel_type": "m.annotation"
}
},
"event_id": "$h29iv0s8:example.com",
"origin_server_ts": 10,
"room_id": "!room:room.com",
"sender": "@carl:example.com",
"type": "m.reaction",
"unsigned": {
"age": 85
}
});
let expected_content = json!({
"m.relates_to": {
"event_id": "$MDitXXXXXX",
"key": "👍",
"rel_type": "m.annotation"
}
});
assert_matches!(
from_json_value::<AnyRoomEventStub>(json_data)
.unwrap(),
AnyRoomEventStub::Message(AnyMessageEventStub::Custom(MessageEventStub {
content: CustomEventContent {
json, event_type,
},
event_id,
origin_server_ts,
sender,
unsigned,
})) if json == expected_content && event_type == "m.reaction"
&& event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& origin_server_ts == UNIX_EPOCH + Duration::from_millis(10)
&& sender == UserId::try_from("@carl:example.com").unwrap()
&& !unsigned.is_empty()
);
}

View File

@ -10,9 +10,8 @@ use ruma_events::{
message::{MessageEventContent, TextMessageEventContent}, message::{MessageEventContent, TextMessageEventContent},
power_levels::PowerLevelsEventContent, power_levels::PowerLevelsEventContent,
}, },
AnyEvent, AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyEvent, AnyMessageEvent, AnyMessageEventStub, AnyRoomEvent, AnyRoomEventStub, AnyStateEvent,
AnyStateEvent, AnyStateEventContent, MessageEvent, MessageEventStub, StateEvent, AnyStateEventStub, MessageEvent, MessageEventStub, StateEvent, StateEventStub,
StateEventStub,
}; };
fn message_event() -> JsonValue { fn message_event() -> JsonValue {
@ -120,12 +119,12 @@ fn power_event_stub_deserialization() {
assert_matches!( assert_matches!(
from_json_value::<AnyRoomEventStub>(json_data), from_json_value::<AnyRoomEventStub>(json_data),
Ok(AnyRoomEventStub::State( Ok(AnyRoomEventStub::State(
StateEventStub { AnyStateEventStub::RoomPowerLevels(StateEventStub {
content: AnyStateEventContent::RoomPowerLevels(PowerLevelsEventContent { content: PowerLevelsEventContent {
ban, .. ban, ..
}), },
.. ..
} })
)) ))
if ban == js_int::Int::new(50).unwrap() if ban == js_int::Int::new(50).unwrap()
); );
@ -138,14 +137,14 @@ fn message_event_stub_deserialization() {
assert_matches!( assert_matches!(
from_json_value::<AnyRoomEventStub>(json_data), from_json_value::<AnyRoomEventStub>(json_data),
Ok(AnyRoomEventStub::Message( Ok(AnyRoomEventStub::Message(
MessageEventStub { AnyMessageEventStub::RoomMessage(MessageEventStub {
content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { content: MessageEventContent::Text(TextMessageEventContent {
body, body,
formatted: Some(formatted), formatted: Some(formatted),
relates_to: None, relates_to: None,
})), }),
.. ..
} })
)) ))
if body == "baba" && formatted.body == "<strong>baba</strong>" if body == "baba" && formatted.body == "<strong>baba</strong>"
); );
@ -158,12 +157,12 @@ fn aliases_event_stub_deserialization() {
assert_matches!( assert_matches!(
from_json_value::<AnyRoomEventStub>(json_data), from_json_value::<AnyRoomEventStub>(json_data),
Ok(AnyRoomEventStub::State( Ok(AnyRoomEventStub::State(
StateEventStub { AnyStateEventStub::RoomAliases(StateEventStub {
content: AnyStateEventContent::RoomAliases(AliasesEventContent { content: AliasesEventContent {
aliases, aliases,
}), },
.. ..
} })
)) ))
if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ]
); );

View File

@ -0,0 +1,73 @@
use std::{
convert::TryFrom,
time::{Duration, UNIX_EPOCH},
};
use matches::assert_matches;
use ruma_events::{
room::redaction::{RedactionEvent, RedactionEventContent},
AnyMessageEvent, EventJson, UnsignedData,
};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::{
from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue,
};
fn redaction() -> JsonValue {
json!({
"content": {
"reason": "being a turd"
},
"redacts": "$nomore:example.com",
"event_id": "$h29iv0s8:example.com",
"sender": "@carl:example.com",
"origin_server_ts": 1,
"room_id": "!roomid:room.com",
"type": "m.room.redaction"
})
}
#[test]
fn serialize_redaction() {
let aliases_event = RedactionEvent {
content: RedactionEventContent { reason: Some("being a turd".to_string()) },
redacts: EventId::try_from("$nomore:example.com").unwrap(),
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
sender: UserId::try_from("@carl:example.com").unwrap(),
unsigned: UnsignedData::default(),
};
let actual = to_json_value(&aliases_event).unwrap();
let expected = redaction();
assert_eq!(actual, expected);
}
#[test]
fn deserialize_redaction() {
let json_data = redaction();
assert_matches!(
from_json_value::<EventJson<AnyMessageEvent>>(json_data)
.unwrap()
.deserialize()
.unwrap(),
AnyMessageEvent::RoomRedaction(RedactionEvent {
content: RedactionEventContent { reason: Some(reas) },
redacts,
event_id,
origin_server_ts,
room_id,
sender,
unsigned,
}) if reas == "being a turd"
&& event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& redacts == EventId::try_from("$nomore:example.com").unwrap()
&& origin_server_ts == UNIX_EPOCH + Duration::from_millis(1)
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
&& sender == UserId::try_from("@carl:example.com").unwrap()
&& unsigned.is_empty()
);
}

View File

@ -3,14 +3,14 @@ use std::convert::TryFrom;
use js_int::uint; use js_int::uint;
use ruma_events::{ use ruma_events::{
room::{join_rules::JoinRule, topic::TopicEventContent}, room::{join_rules::JoinRule, topic::TopicEventContent},
AnyStateEventContent, AnyStrippedStateEventStub, AnyStateEventContent, AnyStrippedStateEventStub, StrippedStateEventStub,
}; };
use ruma_identifiers::UserId; use ruma_identifiers::UserId;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
#[test] #[test]
fn serialize_stripped_state_event() { fn serialize_stripped_state_event_any_content() {
let event = AnyStrippedStateEventStub { let event = StrippedStateEventStub {
content: AnyStateEventContent::RoomTopic(TopicEventContent { content: AnyStateEventContent::RoomTopic(TopicEventContent {
topic: "Testing room".to_string(), topic: "Testing room".to_string(),
}), }),
@ -30,6 +30,26 @@ fn serialize_stripped_state_event() {
assert_eq!(to_json_value(&event).unwrap(), json_data); assert_eq!(to_json_value(&event).unwrap(), json_data);
} }
#[test]
fn serialize_stripped_state_event_any_event() {
let event = AnyStrippedStateEventStub::RoomTopic(StrippedStateEventStub {
content: TopicEventContent { topic: "Testing room".to_string() },
state_key: "".to_string(),
sender: UserId::try_from("@example:localhost").unwrap(),
});
let json_data = json!({
"content": {
"topic": "Testing room"
},
"type": "m.room.topic",
"state_key": "",
"sender": "@example:localhost"
});
assert_eq!(to_json_value(&event).unwrap(), json_data);
}
#[test] #[test]
fn deserialize_stripped_state_events() { fn deserialize_stripped_state_events() {
let name_event = json!({ let name_event = json!({
@ -76,9 +96,9 @@ fn deserialize_stripped_state_events() {
}); });
let event = from_json_value::<AnyStrippedStateEventStub>(name_event).unwrap(); let event = from_json_value::<AnyStrippedStateEventStub>(name_event).unwrap();
match event.content { match event {
AnyStateEventContent::RoomName(content) => { AnyStrippedStateEventStub::RoomName(event) => {
assert_eq!(content.name(), Some("Ruma")); assert_eq!(event.content.name(), Some("Ruma"));
assert_eq!(event.state_key, ""); assert_eq!(event.state_key, "");
assert_eq!(event.sender.to_string(), "@example:localhost"); assert_eq!(event.sender.to_string(), "@example:localhost");
} }
@ -86,9 +106,9 @@ fn deserialize_stripped_state_events() {
} }
let event = from_json_value::<AnyStrippedStateEventStub>(join_rules_event).unwrap(); let event = from_json_value::<AnyStrippedStateEventStub>(join_rules_event).unwrap();
match event.content { match event {
AnyStateEventContent::RoomJoinRules(content) => { AnyStrippedStateEventStub::RoomJoinRules(event) => {
assert_eq!(content.join_rule, JoinRule::Public); assert_eq!(event.content.join_rule, JoinRule::Public);
assert_eq!(event.state_key, ""); assert_eq!(event.state_key, "");
assert_eq!(event.sender.to_string(), "@example:localhost"); assert_eq!(event.sender.to_string(), "@example:localhost");
} }
@ -96,16 +116,16 @@ fn deserialize_stripped_state_events() {
} }
let event = from_json_value::<AnyStrippedStateEventStub>(avatar_event).unwrap(); let event = from_json_value::<AnyStrippedStateEventStub>(avatar_event).unwrap();
match event.content { match event {
AnyStateEventContent::RoomAvatar(content) => { AnyStrippedStateEventStub::RoomAvatar(event) => {
let image_info = content.info.unwrap(); let image_info = event.content.info.unwrap();
assert_eq!(image_info.height.unwrap(), uint!(128)); assert_eq!(image_info.height.unwrap(), uint!(128));
assert_eq!(image_info.width.unwrap(), uint!(128)); assert_eq!(image_info.width.unwrap(), uint!(128));
assert_eq!(image_info.mimetype.unwrap(), "image/jpeg"); assert_eq!(image_info.mimetype.unwrap(), "image/jpeg");
assert_eq!(image_info.size.unwrap(), uint!(1024)); assert_eq!(image_info.size.unwrap(), uint!(1024));
assert_eq!(image_info.thumbnail_info.unwrap().size.unwrap(), uint!(32)); assert_eq!(image_info.thumbnail_info.unwrap().size.unwrap(), uint!(32));
assert_eq!(content.url, "https://example.com/image.jpg"); assert_eq!(event.content.url, "https://example.com/image.jpg");
assert_eq!(event.state_key, ""); assert_eq!(event.state_key, "");
assert_eq!(event.sender.to_string(), "@example:localhost"); assert_eq!(event.sender.to_string(), "@example:localhost");
} }