Revert " Remove event_enum! and only use event_content_enum"
This reverts commit 2a91dc1eb7215a762bd2204bc103ef172258d2d9. Also * Add back type defs and Any*Event enums * Move EventDeHelper and from_raw_json_value to lib make pub so event_enum! macro can use them and test. * Fix Any*Event enum deserialization error * Remove event_content_enum! macro and ruma-events-macros/src/content_enum.rs * Use serde's IgnoreAny to skip Unknown field's value * Clean up imports and test names for state_event
This commit is contained in:
parent
f9639e110e
commit
184aafa5f6
@ -244,7 +244,9 @@ fn expand_deserialize_event(
|
|||||||
|
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match key {
|
match key {
|
||||||
Field::Unknown => continue,
|
Field::Unknown => {
|
||||||
|
let _: ::serde::de::IgnoredAny = map.next_value()?;
|
||||||
|
},
|
||||||
Field::Type => {
|
Field::Type => {
|
||||||
if event_type.is_some() {
|
if event_type.is_some() {
|
||||||
return Err(::serde::de::Error::duplicate_field("type"));
|
return Err(::serde::de::Error::duplicate_field("type"));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Implementation of the content_enum type macro.
|
//! Implementation of event enum and event content enum macros.
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
@ -7,16 +7,69 @@ use syn::{
|
|||||||
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
|
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a content enum from `ContentEnumInput`.
|
/// Create a content enum from `EventEnumInput`.
|
||||||
///
|
pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
|
||||||
/// This is the internals of the `event_content_enum!` macro.
|
|
||||||
pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream> {
|
|
||||||
let attrs = &input.attrs;
|
let attrs = &input.attrs;
|
||||||
let ident = &input.name;
|
let ident = &input.name;
|
||||||
|
|
||||||
let event_type_str = &input.events;
|
let event_type_str = &input.events;
|
||||||
|
|
||||||
let variants = input.events.iter().map(to_camel_case).collect::<Vec<_>>();
|
let variants = input.events.iter().map(to_camel_case).collect::<syn::Result<Vec<_>>>()?;
|
||||||
|
let content = input.events.iter().map(to_event_path).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let event_enum = quote! {
|
||||||
|
#( #attrs )*
|
||||||
|
#[derive(Clone, Debug, ::serde::Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum #ident {
|
||||||
|
#(
|
||||||
|
#[doc = #event_type_str]
|
||||||
|
#variants(#content),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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))
|
||||||
|
},
|
||||||
|
)*
|
||||||
|
_ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let event_content_enum = expand_content_enum(input)?;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
#event_enum
|
||||||
|
|
||||||
|
#event_deserialize_impl
|
||||||
|
|
||||||
|
#event_content_enum
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a content enum from `EventEnumInput`.
|
||||||
|
pub fn expand_content_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
|
||||||
|
let attrs = &input.attrs;
|
||||||
|
let ident = Ident::new(&format!("{}Content", input.name.to_string()), input.name.span());
|
||||||
|
let event_type_str = &input.events;
|
||||||
|
|
||||||
|
let variants = input.events.iter().map(to_camel_case).collect::<syn::Result<Vec<_>>>()?;
|
||||||
let content = input.events.iter().map(to_event_content_path).collect::<Vec<_>>();
|
let content = input.events.iter().map(to_event_content_path).collect::<Vec<_>>();
|
||||||
|
|
||||||
let content_enum = quote! {
|
let content_enum = quote! {
|
||||||
@ -29,8 +82,8 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream>
|
|||||||
#[doc = #event_type_str]
|
#[doc = #event_type_str]
|
||||||
#variants(#content),
|
#variants(#content),
|
||||||
)*
|
)*
|
||||||
#[doc = "Represents any event not defined in the Matrix spec."]
|
/// Any custom event.
|
||||||
Custom(::ruma_events::custom::CustomEventContent)
|
Custom(::ruma_events::custom::CustomEventContent),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,14 +92,11 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream>
|
|||||||
fn event_type(&self) -> &str {
|
fn event_type(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
#( Self::#variants(content) => content.event_type(), )*
|
#( Self::#variants(content) => content.event_type(), )*
|
||||||
#ident::Custom(content) => content.event_type(),
|
Self::Custom(content) => content.event_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_parts(
|
fn from_parts(event_type: &str, input: Box<::serde_json::value::RawValue>) -> Result<Self, ::serde_json::Error> {
|
||||||
event_type: &str,
|
|
||||||
input: Box<::serde_json::value::RawValue>,
|
|
||||||
) -> Result<Self, ::serde_json::Error> {
|
|
||||||
match event_type {
|
match event_type {
|
||||||
#(
|
#(
|
||||||
#event_type_str => {
|
#event_type_str => {
|
||||||
@ -74,7 +124,7 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let marker_trait_impls = marker_traits(ident);
|
let marker_trait_impls = marker_traits(&ident);
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#content_enum
|
#content_enum
|
||||||
@ -107,9 +157,28 @@ fn marker_traits(ident: &Ident) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_event_content_path(
|
fn to_event_path(name: &LitStr) -> TokenStream {
|
||||||
name: &LitStr,
|
let span = name.span();
|
||||||
) -> syn::punctuated::Punctuated<syn::Token![::], syn::PathSegment> {
|
let name = name.value();
|
||||||
|
|
||||||
|
assert_eq!(&name[..2], "m.");
|
||||||
|
|
||||||
|
let path = name[2..].split('.').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let event_str = path.last().unwrap();
|
||||||
|
let event = event_str
|
||||||
|
.split('_')
|
||||||
|
.map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
let content_str = Ident::new(&format!("{}Event", event), span);
|
||||||
|
let path = path.iter().map(|s| Ident::new(s, span));
|
||||||
|
quote! {
|
||||||
|
::ruma_events::#( #path )::*::#content_str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_event_content_path(name: &LitStr) -> TokenStream {
|
||||||
let span = name.span();
|
let span = name.span();
|
||||||
let name = name.value();
|
let name = name.value();
|
||||||
|
|
||||||
@ -125,56 +194,60 @@ fn to_event_content_path(
|
|||||||
|
|
||||||
let content_str = Ident::new(&format!("{}EventContent", event), span);
|
let content_str = Ident::new(&format!("{}EventContent", event), span);
|
||||||
let path = path.iter().map(|s| Ident::new(s, span));
|
let path = path.iter().map(|s| Ident::new(s, span));
|
||||||
syn::parse_quote! {
|
quote! {
|
||||||
::ruma_events::#( #path )::*::#content_str
|
::ruma_events::#( #path )::*::#content_str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits the given `event_type` string on `.` and `_` removing the `m.room.` then
|
/// Splits the given `event_type` string on `.` and `_` removing the `m.room.` then
|
||||||
/// camel casing to give the `EventContent` struct name.
|
/// camel casing to give the `Event` struct name.
|
||||||
pub(crate) fn to_camel_case(name: &LitStr) -> Ident {
|
pub(crate) fn to_camel_case(name: &LitStr) -> syn::Result<Ident> {
|
||||||
let span = name.span();
|
let span = name.span();
|
||||||
let name = name.value();
|
let name = name.value();
|
||||||
|
|
||||||
if &name[..2] != "m." {
|
if &name[..2] != "m." {
|
||||||
panic!("well-known matrix events have to start with `m.` found `{}`", name,)
|
return Err(syn::Error::new(
|
||||||
|
span,
|
||||||
|
format!("well-known matrix events have to start with `m.` found `{}`", name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = name[2..]
|
let s = name[2..]
|
||||||
.split(&['.', '_'] as &[char])
|
.split(&['.', '_'] as &[char])
|
||||||
.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>();
|
||||||
Ident::new(&s, span)
|
|
||||||
|
Ok(Ident::new(&s, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Custom keywords for the `event_content_content_enum!` macro
|
/// Custom keywords for the `event_enum!` macro
|
||||||
mod kw {
|
mod kw {
|
||||||
syn::custom_keyword!(name);
|
syn::custom_keyword!(name);
|
||||||
syn::custom_keyword!(events);
|
syn::custom_keyword!(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The entire `event_content_content_enum!` macro structure directly as it appears in the source code..
|
/// The entire `event_enum!` macro structure directly as it appears in the source code.
|
||||||
pub struct ContentEnumInput {
|
pub struct EventEnumInput {
|
||||||
/// Outer attributes on the field, such as a docstring.
|
/// Outer attributes on the field, such as a docstring.
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
|
|
||||||
/// The name of the event.
|
/// The name of the event.
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
|
|
||||||
/// An array of valid matrix event types. This will generate the variants of the event content type "name".
|
/// An array of valid matrix event types. This will generate the variants of the event type "name".
|
||||||
/// There needs to be a corresponding variant in `ruma_events::EventType` for
|
/// There needs to be a corresponding variant in `ruma_events::EventType` for
|
||||||
/// this event (converted to a valid Rust-style type name by stripping `m.`, replacing the
|
/// this event (converted to a valid Rust-style type name by stripping `m.`, replacing the
|
||||||
/// remaining dots by underscores and then converting from snake_case to CamelCase).
|
/// remaining dots by underscores and then converting from snake_case to CamelCase).
|
||||||
pub events: Vec<LitStr>,
|
pub events: Vec<LitStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ContentEnumInput {
|
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 content_enum enum
|
// the name of our event enum
|
||||||
let name: Ident = input.parse()?;
|
let name: Ident = input.parse()?;
|
||||||
input.parse::<Token![,]>()?;
|
input.parse::<Token![,]>()?;
|
||||||
|
|
@ -11,26 +11,27 @@ use proc_macro::TokenStream;
|
|||||||
use syn::{parse_macro_input, DeriveInput};
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
content_enum::{expand_content_enum, ContentEnumInput},
|
|
||||||
event::expand_event,
|
event::expand_event,
|
||||||
event_content::{
|
event_content::{
|
||||||
expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content,
|
expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content,
|
||||||
expand_message_event_content, expand_room_event_content, expand_state_event_content,
|
expand_message_event_content, expand_room_event_content, expand_state_event_content,
|
||||||
},
|
},
|
||||||
|
event_enum::{expand_event_enum, EventEnumInput},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod content_enum;
|
|
||||||
mod event;
|
mod event;
|
||||||
mod event_content;
|
mod event_content;
|
||||||
|
mod event_enum;
|
||||||
|
|
||||||
/// Generates a content enum to represent the various Matrix event types.
|
/// Generates an enum to represent the various Matrix event types.
|
||||||
///
|
///
|
||||||
/// This macro also implements the necessary traits for the type to serialize and deserialize
|
/// This macro also implements the necessary traits for the type to serialize and deserialize
|
||||||
/// itself.
|
/// itself.
|
||||||
|
// TODO more docs/example
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn event_content_enum(input: TokenStream) -> TokenStream {
|
pub fn event_enum(input: TokenStream) -> TokenStream {
|
||||||
let content_enum_input = syn::parse_macro_input!(input as ContentEnumInput);
|
let event_enum_input = syn::parse_macro_input!(input as EventEnumInput);
|
||||||
expand_content_enum(content_enum_input).unwrap_or_else(|err| err.to_compile_error()).into()
|
expand_event_enum(event_enum_input).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an implementation of `ruma_events::EventContent`.
|
/// Generates an implementation of `ruma_events::EventContent`.
|
||||||
|
@ -1,35 +1,32 @@
|
|||||||
use ruma_events_macros::event_content_enum;
|
use ruma_events_macros::event_enum;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{self, DeserializeOwned, Error as _},
|
de::{self, Error},
|
||||||
Deserialize, Serialize,
|
Serialize,
|
||||||
};
|
};
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event_kinds::{
|
event_kinds::{MessageEventStub, StateEventStub, StrippedStateEventStub},
|
||||||
BasicEvent, EphemeralRoomEvent, MessageEvent, MessageEventStub, StateEvent, StateEventStub,
|
from_raw_json_value, EventDeHelper,
|
||||||
StrippedStateEventStub, ToDeviceEvent,
|
|
||||||
},
|
|
||||||
presence::PresenceEvent,
|
|
||||||
room::redaction::{RedactionEvent, RedactionEventStub},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any basic event.
|
/// Any basic event.
|
||||||
name: AnyBasicEventContent,
|
name: AnyBasicEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.direct",
|
"m.direct",
|
||||||
"m.dummy",
|
"m.dummy",
|
||||||
"m.ignored_user_list",
|
"m.ignored_user_list",
|
||||||
|
"m.presence",
|
||||||
"m.push_rules",
|
"m.push_rules",
|
||||||
"m.room_key",
|
"m.room_key",
|
||||||
"m.tag",
|
"m.tag",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any ephemeral room event.
|
/// Any ephemeral room event.
|
||||||
name: AnyEphemeralRoomEventContent,
|
name: AnyEphemeralRoomEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.fully_read",
|
"m.fully_read",
|
||||||
"m.receipt",
|
"m.receipt",
|
||||||
@ -37,24 +34,25 @@ event_content_enum! {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any message event.
|
/// Any message event.
|
||||||
name: AnyMessageEventContent,
|
name: AnyMessageEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.call.answer",
|
"m.call.answer",
|
||||||
"m.call.invite",
|
"m.call.invite",
|
||||||
"m.call.hangup",
|
"m.call.hangup",
|
||||||
"m.call.candidates",
|
"m.call.candidates",
|
||||||
"m.room.encrypted",
|
|
||||||
"m.room.message",
|
"m.room.message",
|
||||||
"m.room.message.feedback",
|
"m.room.message.feedback",
|
||||||
|
"m.room.redaction",
|
||||||
"m.sticker",
|
"m.sticker",
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any state event.
|
/// Any state event.
|
||||||
name: AnyStateEventContent,
|
name: AnyStateEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.room.aliases",
|
"m.room.aliases",
|
||||||
"m.room.avatar",
|
"m.room.avatar",
|
||||||
@ -76,9 +74,9 @@ event_content_enum! {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any to-device event.
|
/// Any to-device event.
|
||||||
name: AnyToDeviceEventContent,
|
name: AnyToDeviceEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.dummy",
|
"m.dummy",
|
||||||
"m.room_key",
|
"m.room_key",
|
||||||
@ -94,21 +92,9 @@ event_content_enum! {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Any basic event, one that has no (well-known) fields outside of `content`.
|
|
||||||
pub type AnyBasicEvent = BasicEvent<AnyBasicEventContent>;
|
|
||||||
|
|
||||||
/// Any ephemeral room event.
|
|
||||||
pub type AnyEphemeralRoomEvent = EphemeralRoomEvent<AnyEphemeralRoomEventContent>;
|
|
||||||
|
|
||||||
/// Any message event.
|
|
||||||
pub type AnyMessageEvent = MessageEvent<AnyMessageEventContent>;
|
|
||||||
|
|
||||||
/// Any message event stub (message event without a `room_id`, as returned in `/sync` responses)
|
/// Any message event stub (message event without a `room_id`, as returned in `/sync` responses)
|
||||||
pub type AnyMessageEventStub = MessageEventStub<AnyMessageEventContent>;
|
pub type AnyMessageEventStub = MessageEventStub<AnyMessageEventContent>;
|
||||||
|
|
||||||
/// Any state event.
|
|
||||||
pub type AnyStateEvent = StateEvent<AnyStateEventContent>;
|
|
||||||
|
|
||||||
/// Any state event stub (state event without a `room_id`, as returned in `/sync` responses)
|
/// Any state event stub (state event without a `room_id`, as returned in `/sync` responses)
|
||||||
pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>;
|
pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>;
|
||||||
|
|
||||||
@ -116,23 +102,16 @@ pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>;
|
|||||||
/// been invited to in `/sync` responses)
|
/// been invited to in `/sync` responses)
|
||||||
pub type AnyStrippedStateEventStub = StrippedStateEventStub<AnyStateEventContent>;
|
pub type AnyStrippedStateEventStub = StrippedStateEventStub<AnyStateEventContent>;
|
||||||
|
|
||||||
/// Any to-device event.
|
|
||||||
pub type AnyToDeviceEvent = ToDeviceEvent<AnyToDeviceEventContent>;
|
|
||||||
|
|
||||||
/// Any event.
|
/// Any event.
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum AnyEvent {
|
pub enum AnyEvent {
|
||||||
/// Any basic event.
|
/// Any basic event.
|
||||||
Basic(AnyBasicEvent),
|
Basic(AnyBasicEvent),
|
||||||
/// `"m.presence"`, the only non-room event with a `sender` field.
|
|
||||||
Presence(PresenceEvent),
|
|
||||||
/// Any ephemeral room event.
|
/// Any ephemeral room event.
|
||||||
Ephemeral(AnyEphemeralRoomEvent),
|
Ephemeral(AnyEphemeralRoomEvent),
|
||||||
/// Any message event.
|
/// Any message event.
|
||||||
Message(AnyMessageEvent),
|
Message(AnyMessageEvent),
|
||||||
/// `"m.room.redaction"`, the only room event with a `redacts` field.
|
|
||||||
Redaction(RedactionEvent),
|
|
||||||
/// Any state event.
|
/// Any state event.
|
||||||
State(AnyStateEvent),
|
State(AnyStateEvent),
|
||||||
}
|
}
|
||||||
@ -143,8 +122,6 @@ pub enum AnyEvent {
|
|||||||
pub enum AnyRoomEvent {
|
pub enum AnyRoomEvent {
|
||||||
/// Any message event.
|
/// Any message event.
|
||||||
Message(AnyMessageEvent),
|
Message(AnyMessageEvent),
|
||||||
/// `"m.room.redaction"`, the only room event with a `redacts` field.
|
|
||||||
Redaction(RedactionEvent),
|
|
||||||
/// Any state event.
|
/// Any state event.
|
||||||
State(AnyStateEvent),
|
State(AnyStateEvent),
|
||||||
}
|
}
|
||||||
@ -155,26 +132,10 @@ pub enum AnyRoomEvent {
|
|||||||
pub enum AnyRoomEventStub {
|
pub enum AnyRoomEventStub {
|
||||||
/// Any message event stub
|
/// Any message event stub
|
||||||
Message(AnyMessageEventStub),
|
Message(AnyMessageEventStub),
|
||||||
/// `"m.room.redaction"` stub
|
|
||||||
Redaction(RedactionEventStub),
|
|
||||||
/// Any state event stub
|
/// Any state event stub
|
||||||
State(AnyStateEventStub),
|
State(AnyStateEventStub),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct EventDeHelper {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
ev_type: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_raw_json_value<T, E>(val: &RawJsonValue) -> Result<T, E>
|
|
||||||
where
|
|
||||||
T: DeserializeOwned,
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
serde_json::from_str(val.get()).map_err(E::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME `#[serde(untagged)]` deserialization fails for these enums which
|
// FIXME `#[serde(untagged)]` deserialization fails for these enums which
|
||||||
// is odd as we are doing basically the same thing here, investigate?
|
// is odd as we are doing basically the same thing here, investigate?
|
||||||
impl<'de> de::Deserialize<'de> for AnyEvent {
|
impl<'de> de::Deserialize<'de> for AnyEvent {
|
||||||
@ -186,8 +147,6 @@ impl<'de> de::Deserialize<'de> for AnyEvent {
|
|||||||
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
||||||
|
|
||||||
match ev_type.as_str() {
|
match ev_type.as_str() {
|
||||||
"m.room.redaction" => Ok(AnyEvent::Redaction(from_raw_json_value(&json)?)),
|
|
||||||
"m.presence" => Ok(AnyEvent::Presence(from_raw_json_value(&json)?)),
|
|
||||||
ev_type if AnyBasicEventContent::is_compatible(ev_type) => {
|
ev_type if AnyBasicEventContent::is_compatible(ev_type) => {
|
||||||
Ok(AnyEvent::Basic(from_raw_json_value(&json)?))
|
Ok(AnyEvent::Basic(from_raw_json_value(&json)?))
|
||||||
}
|
}
|
||||||
@ -214,7 +173,6 @@ impl<'de> de::Deserialize<'de> for AnyRoomEvent {
|
|||||||
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
||||||
|
|
||||||
match ev_type.as_str() {
|
match ev_type.as_str() {
|
||||||
"m.room.redaction" => Ok(AnyRoomEvent::Redaction(from_raw_json_value(&json)?)),
|
|
||||||
ev_type if AnyMessageEventContent::is_compatible(ev_type) => {
|
ev_type if AnyMessageEventContent::is_compatible(ev_type) => {
|
||||||
Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?))
|
Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?))
|
||||||
}
|
}
|
||||||
@ -235,7 +193,6 @@ impl<'de> de::Deserialize<'de> for AnyRoomEventStub {
|
|||||||
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
let EventDeHelper { ev_type } = from_raw_json_value(&json)?;
|
||||||
|
|
||||||
match ev_type.as_str() {
|
match ev_type.as_str() {
|
||||||
"m.room.redaction" => Ok(AnyRoomEventStub::Redaction(from_raw_json_value(&json)?)),
|
|
||||||
ev_type if AnyMessageEventContent::is_compatible(ev_type) => {
|
ev_type if AnyMessageEventContent::is_compatible(ev_type) => {
|
||||||
Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?))
|
Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?))
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use js_int::Int;
|
use js_int::Int;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de, 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,3 +234,21 @@ 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.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct EventDeHelper {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub ev_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function for serde_json::value::RawValue deserialization.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn from_raw_json_value<T, E>(val: &RawJsonValue) -> Result<T, E>
|
||||||
|
where
|
||||||
|
T: de::DeserializeOwned,
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
serde_json::from_str(val.get()).map_err(E::custom)
|
||||||
|
}
|
||||||
|
@ -10,8 +10,9 @@ use ruma_events::{
|
|||||||
message::{MessageEventContent, TextMessageEventContent},
|
message::{MessageEventContent, TextMessageEventContent},
|
||||||
power_levels::PowerLevelsEventContent,
|
power_levels::PowerLevelsEventContent,
|
||||||
},
|
},
|
||||||
AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent,
|
AnyEvent, AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub,
|
||||||
MessageEvent, MessageEventStub, StateEvent, StateEventStub,
|
AnyStateEvent, AnyStateEventContent, MessageEvent, MessageEventStub, StateEvent,
|
||||||
|
StateEventStub,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn message_event() -> JsonValue {
|
fn message_event() -> JsonValue {
|
||||||
@ -175,14 +176,14 @@ fn message_room_event_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<AnyRoomEvent>(json_data),
|
from_json_value::<AnyRoomEvent>(json_data),
|
||||||
Ok(AnyRoomEvent::Message(
|
Ok(AnyRoomEvent::Message(
|
||||||
MessageEvent {
|
AnyMessageEvent::RoomMessage(MessageEvent {
|
||||||
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>"
|
||||||
);
|
);
|
||||||
@ -195,12 +196,12 @@ fn alias_room_event_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<AnyRoomEvent>(json_data),
|
from_json_value::<AnyRoomEvent>(json_data),
|
||||||
Ok(AnyRoomEvent::State(
|
Ok(AnyRoomEvent::State(
|
||||||
StateEvent {
|
AnyStateEvent::RoomAliases(StateEvent {
|
||||||
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() ]
|
||||||
);
|
);
|
||||||
@ -213,14 +214,14 @@ fn message_event_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<AnyEvent>(json_data),
|
from_json_value::<AnyEvent>(json_data),
|
||||||
Ok(AnyEvent::Message(
|
Ok(AnyEvent::Message(
|
||||||
MessageEvent {
|
AnyMessageEvent::RoomMessage(MessageEvent {
|
||||||
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>"
|
||||||
);
|
);
|
||||||
@ -233,12 +234,12 @@ fn alias_event_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<AnyEvent>(json_data),
|
from_json_value::<AnyEvent>(json_data),
|
||||||
Ok(AnyEvent::State(
|
Ok(AnyEvent::State(
|
||||||
StateEvent {
|
AnyStateEvent::RoomAliases(StateEvent {
|
||||||
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() ]
|
||||||
);
|
);
|
||||||
|
124
ruma-events/tests/event_enums.rs
Normal file
124
ruma-events/tests/event_enums.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use std::{
|
||||||
|
convert::TryFrom,
|
||||||
|
time::{Duration, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use matches::assert_matches;
|
||||||
|
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
|
use ruma_events::{
|
||||||
|
call::{answer::AnswerEventContent, SessionDescription, SessionDescriptionType},
|
||||||
|
room::{ImageInfo, ThumbnailInfo},
|
||||||
|
sticker::StickerEventContent,
|
||||||
|
AnyMessageEvent, MessageEvent, UnsignedData,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ui() {
|
||||||
|
let t = trybuild::TestCases::new();
|
||||||
|
t.pass("tests/ui/07-enum-sanity-check.rs");
|
||||||
|
t.compile_fail("tests/ui/08-enum-invalid-path.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_message_event() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"answer": {
|
||||||
|
"type": "answer",
|
||||||
|
"sdp": "Hello"
|
||||||
|
},
|
||||||
|
"call_id": "foofoo",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"event_id": "$h29iv0s8:example.com",
|
||||||
|
"origin_server_ts": 1,
|
||||||
|
"room_id": "!roomid:room.com",
|
||||||
|
"sender": "@carl:example.com",
|
||||||
|
"type": "m.call.answer"
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AnyMessageEvent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
AnyMessageEvent::CallAnswer(MessageEvent {
|
||||||
|
content: AnswerEventContent {
|
||||||
|
answer: SessionDescription {
|
||||||
|
session_type: SessionDescriptionType::Answer,
|
||||||
|
sdp,
|
||||||
|
},
|
||||||
|
call_id,
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
event_id,
|
||||||
|
origin_server_ts,
|
||||||
|
room_id,
|
||||||
|
sender,
|
||||||
|
unsigned,
|
||||||
|
}) if sdp == "Hello" && call_id == "foofoo" && version == UInt::new(1).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.is_empty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_message_event() {
|
||||||
|
let aliases_event = AnyMessageEvent::Sticker(MessageEvent {
|
||||||
|
content: StickerEventContent {
|
||||||
|
body: "Hello".into(),
|
||||||
|
info: ImageInfo {
|
||||||
|
height: UInt::new(423),
|
||||||
|
width: UInt::new(1011),
|
||||||
|
mimetype: Some("image/png".into()),
|
||||||
|
size: UInt::new(84242),
|
||||||
|
thumbnail_info: Some(Box::new(ThumbnailInfo {
|
||||||
|
width: UInt::new(800),
|
||||||
|
height: UInt::new(334),
|
||||||
|
mimetype: Some("image/png".into()),
|
||||||
|
size: UInt::new(82595),
|
||||||
|
})),
|
||||||
|
thumbnail_url: Some("mxc://matrix.org".into()),
|
||||||
|
thumbnail_file: None,
|
||||||
|
},
|
||||||
|
url: "http://www.matrix.org".into(),
|
||||||
|
},
|
||||||
|
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 = json!({
|
||||||
|
"content": {
|
||||||
|
"body": "Hello",
|
||||||
|
"info": {
|
||||||
|
"h": 423,
|
||||||
|
"mimetype": "image/png",
|
||||||
|
"size": 84242,
|
||||||
|
"thumbnail_info": {
|
||||||
|
"h": 334,
|
||||||
|
"mimetype": "image/png",
|
||||||
|
"size": 82595,
|
||||||
|
"w": 800
|
||||||
|
},
|
||||||
|
"thumbnail_url": "mxc://matrix.org",
|
||||||
|
"w": 1011
|
||||||
|
},
|
||||||
|
"url": "http://www.matrix.org"
|
||||||
|
},
|
||||||
|
"event_id": "$h29iv0s8:example.com",
|
||||||
|
"origin_server_ts": 1,
|
||||||
|
"room_id": "!roomid:room.com",
|
||||||
|
"sender": "@carl:example.com",
|
||||||
|
"type": "m.sticker",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
@ -7,7 +7,8 @@ use js_int::UInt;
|
|||||||
use matches::assert_matches;
|
use matches::assert_matches;
|
||||||
use ruma_events::{
|
use ruma_events::{
|
||||||
room::{aliases::AliasesEventContent, avatar::AvatarEventContent, ImageInfo, ThumbnailInfo},
|
room::{aliases::AliasesEventContent, avatar::AvatarEventContent, ImageInfo, ThumbnailInfo},
|
||||||
AnyStateEventContent, EventJson, StateEvent, StateEventStub, UnsignedData,
|
AnyRoomEvent, AnyStateEvent, AnyStateEventContent, EventJson, StateEvent, StateEventStub,
|
||||||
|
UnsignedData,
|
||||||
};
|
};
|
||||||
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
||||||
use serde_json::{
|
use serde_json::{
|
||||||
@ -236,3 +237,48 @@ fn deserialize_avatar_without_prev_content() {
|
|||||||
&& unsigned.is_empty()
|
&& unsigned.is_empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_member_event_with_top_level_membership_field() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": "example",
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "$h29iv0s8:example.com",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!room:localhost",
|
||||||
|
"origin_server_ts": 1,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"type": "m.room.member",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1,
|
||||||
|
"replaces_state": "$151800111315tsynI:localhost",
|
||||||
|
"prev_content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": "example",
|
||||||
|
"membership": "invite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AnyRoomEvent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
AnyRoomEvent::State(
|
||||||
|
AnyStateEvent::RoomMember(StateEvent {
|
||||||
|
content,
|
||||||
|
event_id,
|
||||||
|
origin_server_ts,
|
||||||
|
prev_content: None,
|
||||||
|
sender,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
|
||||||
|
&& origin_server_ts == UNIX_EPOCH + Duration::from_millis(1)
|
||||||
|
&& sender == UserId::try_from("@example:localhost").unwrap()
|
||||||
|
&& content.displayname == Some("example".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use ruma_events_macros::event_content_enum;
|
use ruma_events_macros::event_enum;
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
/// Any basic event.
|
/// Any basic event.
|
||||||
name: AnyBasicEventContent,
|
name: AnyBasicEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.direct",
|
"m.direct",
|
||||||
"m.dummy",
|
"m.dummy",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use ruma_events_macros::event_content_enum;
|
use ruma_events_macros::event_enum;
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
name: InvalidEvent,
|
name: InvalidEvent,
|
||||||
events: [
|
events: [
|
||||||
"m.not.a.path",
|
"m.not.a.path",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
event_content_enum! {
|
event_enum! {
|
||||||
name: InvalidEvent,
|
name: InvalidEvent,
|
||||||
events: [
|
events: [
|
||||||
"not.a.path",
|
"not.a.path",
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
error: proc macro panicked
|
error: well-known matrix events have to start with `m.` found `not.a.path`
|
||||||
--> $DIR/08-enum-invalid-path.rs:10:1
|
--> $DIR/08-enum-invalid-path.rs:13:9
|
||||||
|
|
|
|
||||||
10 | / event_content_enum! {
|
13 | "not.a.path",
|
||||||
11 | | name: InvalidEvent,
|
| ^^^^^^^^^^^^
|
||||||
12 | | events: [
|
|
||||||
13 | | "not.a.path",
|
|
||||||
14 | | ]
|
|
||||||
15 | | }
|
|
||||||
| |_^
|
|
||||||
|
|
|
||||||
= help: message: well-known matrix events have to start with `m.` found `not.a.path`
|
|
||||||
|
|
||||||
error[E0433]: failed to resolve: could not find `not` in `ruma_events`
|
error[E0433]: failed to resolve: could not find `not` in `ruma_events`
|
||||||
--> $DIR/08-enum-invalid-path.rs:6:9
|
--> $DIR/08-enum-invalid-path.rs:6:9
|
||||||
|
Loading…
x
Reference in New Issue
Block a user