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:
parent
75ea42961f
commit
fdf87a38a2
@ -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
|
||||||
|
@ -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() {
|
||||||
}
|
|
||||||
ev_type if AnyEphemeralRoomEventContent::is_compatible(ev_type) => {
|
|
||||||
Ok(AnyEvent::Ephemeral(from_raw_json_value(&json)?))
|
|
||||||
}
|
|
||||||
ev_type if AnyMessageEventContent::is_compatible(ev_type) => {
|
|
||||||
Ok(AnyEvent::Message(from_raw_json_value(&json)?))
|
|
||||||
}
|
|
||||||
ev_type if AnyStateEventContent::is_compatible(ev_type) => {
|
|
||||||
Ok(AnyEvent::State(from_raw_json_value(&json)?))
|
Ok(AnyEvent::State(from_raw_json_value(&json)?))
|
||||||
}
|
} else if event_id.is_some() {
|
||||||
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))),
|
Ok(AnyEvent::Message(from_raw_json_value(&json)?))
|
||||||
|
} else if room_id.is_some() {
|
||||||
|
Ok(AnyEvent::Ephemeral(from_raw_json_value(&json)?))
|
||||||
|
} else {
|
||||||
|
Ok(AnyEvent::Basic(from_raw_json_value(&json)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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::Message(from_raw_json_value(&json)?))
|
|
||||||
}
|
|
||||||
ev_type if AnyStateEventContent::is_compatible(ev_type) => {
|
|
||||||
Ok(AnyRoomEvent::State(from_raw_json_value(&json)?))
|
Ok(AnyRoomEvent::State(from_raw_json_value(&json)?))
|
||||||
}
|
} else {
|
||||||
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))),
|
Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)?))
|
||||||
|
} else {
|
||||||
Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?))
|
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))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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::{
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -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() ]
|
||||||
);
|
);
|
||||||
|
73
ruma-events/tests/redaction.rs
Normal file
73
ruma-events/tests/redaction.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
@ -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");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user