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()? { | ||||
|                             match key { | ||||
|                                 Field::Unknown => continue, | ||||
|                                 Field::Unknown => { | ||||
|                                     let _: ::serde::de::IgnoredAny = map.next_value()?; | ||||
|                                 }, | ||||
|                                 Field::Type => { | ||||
|                                     if event_type.is_some() { | ||||
|                                         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 quote::quote; | ||||
| @ -7,16 +7,69 @@ use syn::{ | ||||
|     Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token, | ||||
| }; | ||||
| 
 | ||||
| /// Create a content enum from `ContentEnumInput`.
 | ||||
| ///
 | ||||
| /// This is the internals of the `event_content_enum!` macro.
 | ||||
| pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream> { | ||||
| /// Create a content enum from `EventEnumInput`.
 | ||||
| pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> { | ||||
|     let attrs = &input.attrs; | ||||
|     let ident = &input.name; | ||||
| 
 | ||||
|     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_enum = quote! { | ||||
| @ -29,8 +82,8 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream> | ||||
|                 #[doc = #event_type_str] | ||||
|                 #variants(#content), | ||||
|             )* | ||||
|             #[doc = "Represents any event not defined in the Matrix spec."] | ||||
|             Custom(::ruma_events::custom::CustomEventContent) | ||||
|             /// Any custom event.
 | ||||
|             Custom(::ruma_events::custom::CustomEventContent), | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| @ -39,14 +92,11 @@ pub fn expand_content_enum(input: ContentEnumInput) -> syn::Result<TokenStream> | ||||
|             fn event_type(&self) -> &str { | ||||
|                 match self { | ||||
|                     #( Self::#variants(content) => content.event_type(), )* | ||||
|                     #ident::Custom(content) => content.event_type(), | ||||
|                     Self::Custom(content) => content.event_type(), | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             fn from_parts( | ||||
|                 event_type: &str, | ||||
|                 input: Box<::serde_json::value::RawValue>, | ||||
|             ) -> Result<Self, ::serde_json::Error> { | ||||
|             fn from_parts(event_type: &str, input: Box<::serde_json::value::RawValue>) -> Result<Self, ::serde_json::Error> { | ||||
|                 match event_type { | ||||
|                     #( | ||||
|                         #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! { | ||||
|         #content_enum | ||||
| @ -107,9 +157,28 @@ fn marker_traits(ident: &Ident) -> TokenStream { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn to_event_content_path( | ||||
|     name: &LitStr, | ||||
| ) -> syn::punctuated::Punctuated<syn::Token![::], syn::PathSegment> { | ||||
| fn to_event_path(name: &LitStr) -> TokenStream { | ||||
|     let span = name.span(); | ||||
|     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 name = name.value(); | ||||
| 
 | ||||
| @ -125,56 +194,60 @@ fn to_event_content_path( | ||||
| 
 | ||||
|     let content_str = Ident::new(&format!("{}EventContent", event), span); | ||||
|     let path = path.iter().map(|s| Ident::new(s, span)); | ||||
|     syn::parse_quote! { | ||||
|     quote! { | ||||
|         ::ruma_events::#( #path )::*::#content_str | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Splits the given `event_type` string on `.` and `_` removing the `m.room.` then
 | ||||
| /// camel casing to give the `EventContent` struct name.
 | ||||
| pub(crate) fn to_camel_case(name: &LitStr) -> Ident { | ||||
| /// camel casing to give the `Event` struct name.
 | ||||
| pub(crate) fn to_camel_case(name: &LitStr) -> syn::Result<Ident> { | ||||
|     let span = name.span(); | ||||
|     let name = name.value(); | ||||
| 
 | ||||
|     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..] | ||||
|         .split(&['.', '_'] as &[char]) | ||||
|         .map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..]) | ||||
|         .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 { | ||||
|     syn::custom_keyword!(name); | ||||
|     syn::custom_keyword!(events); | ||||
| } | ||||
| 
 | ||||
| /// The entire `event_content_content_enum!` macro structure directly as it appears in the source code..
 | ||||
| pub struct ContentEnumInput { | ||||
| /// The entire `event_enum!` macro structure directly as it appears in the source code.
 | ||||
| pub struct EventEnumInput { | ||||
|     /// Outer attributes on the field, such as a docstring.
 | ||||
|     pub attrs: Vec<Attribute>, | ||||
| 
 | ||||
|     /// The name of the event.
 | ||||
|     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
 | ||||
|     /// 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).
 | ||||
|     pub events: Vec<LitStr>, | ||||
| } | ||||
| 
 | ||||
| impl Parse for ContentEnumInput { | ||||
| impl Parse for EventEnumInput { | ||||
|     fn parse(input: ParseStream<'_>) -> parse::Result<Self> { | ||||
|         let attrs = input.call(Attribute::parse_outer)?; | ||||
|         // name field
 | ||||
|         input.parse::<kw::name>()?; | ||||
|         input.parse::<Token![:]>()?; | ||||
|         // the name of our content_enum enum
 | ||||
|         // the name of our event enum
 | ||||
|         let name: Ident = input.parse()?; | ||||
|         input.parse::<Token![,]>()?; | ||||
| 
 | ||||
| @ -11,26 +11,27 @@ use proc_macro::TokenStream; | ||||
| use syn::{parse_macro_input, DeriveInput}; | ||||
| 
 | ||||
| use self::{ | ||||
|     content_enum::{expand_content_enum, ContentEnumInput}, | ||||
|     event::expand_event, | ||||
|     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, | ||||
|     }, | ||||
|     event_enum::{expand_event_enum, EventEnumInput}, | ||||
| }; | ||||
| 
 | ||||
| mod content_enum; | ||||
| mod event; | ||||
| 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
 | ||||
| /// itself.
 | ||||
| // TODO more docs/example
 | ||||
| #[proc_macro] | ||||
| pub fn event_content_enum(input: TokenStream) -> TokenStream { | ||||
|     let content_enum_input = syn::parse_macro_input!(input as ContentEnumInput); | ||||
|     expand_content_enum(content_enum_input).unwrap_or_else(|err| err.to_compile_error()).into() | ||||
| pub fn event_enum(input: TokenStream) -> TokenStream { | ||||
|     let event_enum_input = syn::parse_macro_input!(input as EventEnumInput); | ||||
|     expand_event_enum(event_enum_input).unwrap_or_else(|err| err.to_compile_error()).into() | ||||
| } | ||||
| 
 | ||||
| /// 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::{ | ||||
|     de::{self, DeserializeOwned, Error as _}, | ||||
|     Deserialize, Serialize, | ||||
|     de::{self, Error}, | ||||
|     Serialize, | ||||
| }; | ||||
| use serde_json::value::RawValue as RawJsonValue; | ||||
| 
 | ||||
| use crate::{ | ||||
|     event_kinds::{ | ||||
|         BasicEvent, EphemeralRoomEvent, MessageEvent, MessageEventStub, StateEvent, StateEventStub, | ||||
|         StrippedStateEventStub, ToDeviceEvent, | ||||
|     }, | ||||
|     presence::PresenceEvent, | ||||
|     room::redaction::{RedactionEvent, RedactionEventStub}, | ||||
|     event_kinds::{MessageEventStub, StateEventStub, StrippedStateEventStub}, | ||||
|     from_raw_json_value, EventDeHelper, | ||||
| }; | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     /// Any basic event.
 | ||||
|     name: AnyBasicEventContent, | ||||
|     name: AnyBasicEvent, | ||||
|     events: [ | ||||
|         "m.direct", | ||||
|         "m.dummy", | ||||
|         "m.ignored_user_list", | ||||
|         "m.presence", | ||||
|         "m.push_rules", | ||||
|         "m.room_key", | ||||
|         "m.tag", | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     /// Any ephemeral room event.
 | ||||
|     name: AnyEphemeralRoomEventContent, | ||||
|     name: AnyEphemeralRoomEvent, | ||||
|     events: [ | ||||
|         "m.fully_read", | ||||
|         "m.receipt", | ||||
| @ -37,24 +34,25 @@ event_content_enum! { | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     /// Any message event.
 | ||||
|     name: AnyMessageEventContent, | ||||
|     name: AnyMessageEvent, | ||||
|     events: [ | ||||
|         "m.call.answer", | ||||
|         "m.call.invite", | ||||
|         "m.call.hangup", | ||||
|         "m.call.candidates", | ||||
|         "m.room.encrypted", | ||||
|         "m.room.message", | ||||
|         "m.room.message.feedback", | ||||
|         "m.room.redaction", | ||||
|         "m.sticker", | ||||
| 
 | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     /// Any state event.
 | ||||
|     name: AnyStateEventContent, | ||||
|     name: AnyStateEvent, | ||||
|     events: [ | ||||
|         "m.room.aliases", | ||||
|         "m.room.avatar", | ||||
| @ -76,9 +74,9 @@ event_content_enum! { | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     /// Any to-device event.
 | ||||
|     name: AnyToDeviceEventContent, | ||||
|     name: AnyToDeviceEvent, | ||||
|     events: [ | ||||
|         "m.dummy", | ||||
|         "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)
 | ||||
| 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)
 | ||||
| pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>; | ||||
| 
 | ||||
| @ -116,23 +102,16 @@ pub type AnyStateEventStub = StateEventStub<AnyStateEventContent>; | ||||
| /// been invited to in `/sync` responses)
 | ||||
| pub type AnyStrippedStateEventStub = StrippedStateEventStub<AnyStateEventContent>; | ||||
| 
 | ||||
| /// Any to-device event.
 | ||||
| pub type AnyToDeviceEvent = ToDeviceEvent<AnyToDeviceEventContent>; | ||||
| 
 | ||||
| /// Any event.
 | ||||
| #[derive(Clone, Debug, Serialize)] | ||||
| #[serde(untagged)] | ||||
| pub enum AnyEvent { | ||||
|     /// Any basic event.
 | ||||
|     Basic(AnyBasicEvent), | ||||
|     /// `"m.presence"`, the only non-room event with a `sender` field.
 | ||||
|     Presence(PresenceEvent), | ||||
|     /// Any ephemeral room event.
 | ||||
|     Ephemeral(AnyEphemeralRoomEvent), | ||||
|     /// Any message event.
 | ||||
|     Message(AnyMessageEvent), | ||||
|     /// `"m.room.redaction"`, the only room event with a `redacts` field.
 | ||||
|     Redaction(RedactionEvent), | ||||
|     /// Any state event.
 | ||||
|     State(AnyStateEvent), | ||||
| } | ||||
| @ -143,8 +122,6 @@ pub enum AnyEvent { | ||||
| pub enum AnyRoomEvent { | ||||
|     /// Any message event.
 | ||||
|     Message(AnyMessageEvent), | ||||
|     /// `"m.room.redaction"`, the only room event with a `redacts` field.
 | ||||
|     Redaction(RedactionEvent), | ||||
|     /// Any state event.
 | ||||
|     State(AnyStateEvent), | ||||
| } | ||||
| @ -155,26 +132,10 @@ pub enum AnyRoomEvent { | ||||
| pub enum AnyRoomEventStub { | ||||
|     /// Any message event stub
 | ||||
|     Message(AnyMessageEventStub), | ||||
|     /// `"m.room.redaction"` stub
 | ||||
|     Redaction(RedactionEventStub), | ||||
|     /// Any state event stub
 | ||||
|     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
 | ||||
| // is odd as we are doing basically the same thing here, investigate?
 | ||||
| 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)?; | ||||
| 
 | ||||
|         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) => { | ||||
|                 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)?; | ||||
| 
 | ||||
|         match ev_type.as_str() { | ||||
|             "m.room.redaction" => Ok(AnyRoomEvent::Redaction(from_raw_json_value(&json)?)), | ||||
|             ev_type if AnyMessageEventContent::is_compatible(ev_type) => { | ||||
|                 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)?; | ||||
| 
 | ||||
|         match ev_type.as_str() { | ||||
|             "m.room.redaction" => Ok(AnyRoomEventStub::Redaction(from_raw_json_value(&json)?)), | ||||
|             ev_type if AnyMessageEventContent::is_compatible(ev_type) => { | ||||
|                 Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?)) | ||||
|             } | ||||
|  | ||||
| @ -120,7 +120,7 @@ | ||||
| use std::fmt::Debug; | ||||
| 
 | ||||
| use js_int::Int; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde::{de, Deserialize, Serialize}; | ||||
| use serde_json::value::RawValue as RawJsonValue; | ||||
| 
 | ||||
| use self::room::redaction::RedactionEvent; | ||||
| @ -234,3 +234,21 @@ pub trait MessageEventContent: RoomEventContent {} | ||||
| 
 | ||||
| /// Marker trait for the content of a state event.
 | ||||
| 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}, | ||||
|         power_levels::PowerLevelsEventContent, | ||||
|     }, | ||||
|     AnyEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, AnyStateEventContent, | ||||
|     MessageEvent, MessageEventStub, StateEvent, StateEventStub, | ||||
|     AnyEvent, AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, AnyRoomEventStub, | ||||
|     AnyStateEvent, AnyStateEventContent, MessageEvent, MessageEventStub, StateEvent, | ||||
|     StateEventStub, | ||||
| }; | ||||
| 
 | ||||
| fn message_event() -> JsonValue { | ||||
| @ -175,14 +176,14 @@ fn message_room_event_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<AnyRoomEvent>(json_data), | ||||
|         Ok(AnyRoomEvent::Message( | ||||
|             MessageEvent { | ||||
|                 content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { | ||||
|             AnyMessageEvent::RoomMessage(MessageEvent { | ||||
|                 content: MessageEventContent::Text(TextMessageEventContent { | ||||
|                     body, | ||||
|                     formatted: Some(formatted), | ||||
|                     relates_to: None, | ||||
|                 })), | ||||
|                 }), | ||||
|                 .. | ||||
|             } | ||||
|             }) | ||||
|         )) | ||||
|         if body == "baba" && formatted.body == "<strong>baba</strong>" | ||||
|     ); | ||||
| @ -195,12 +196,12 @@ fn alias_room_event_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<AnyRoomEvent>(json_data), | ||||
|         Ok(AnyRoomEvent::State( | ||||
|             StateEvent { | ||||
|                 content: AnyStateEventContent::RoomAliases(AliasesEventContent { | ||||
|             AnyStateEvent::RoomAliases(StateEvent { | ||||
|                 content: AliasesEventContent { | ||||
|                     aliases, | ||||
|                 }), | ||||
|                 }, | ||||
|                 .. | ||||
|             } | ||||
|             }) | ||||
|         )) | ||||
|         if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ] | ||||
|     ); | ||||
| @ -213,14 +214,14 @@ fn message_event_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<AnyEvent>(json_data), | ||||
|         Ok(AnyEvent::Message( | ||||
|             MessageEvent { | ||||
|                 content: AnyMessageEventContent::RoomMessage(MessageEventContent::Text(TextMessageEventContent { | ||||
|             AnyMessageEvent::RoomMessage(MessageEvent { | ||||
|                 content: MessageEventContent::Text(TextMessageEventContent { | ||||
|                     body, | ||||
|                     formatted: Some(formatted), | ||||
|                     relates_to: None, | ||||
|                 })), | ||||
|                 }), | ||||
|                 .. | ||||
|             } | ||||
|             }) | ||||
|         )) | ||||
|         if body == "baba" && formatted.body == "<strong>baba</strong>" | ||||
|     ); | ||||
| @ -233,12 +234,12 @@ fn alias_event_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<AnyEvent>(json_data), | ||||
|         Ok(AnyEvent::State( | ||||
|             StateEvent { | ||||
|                 content: AnyStateEventContent::RoomAliases(AliasesEventContent { | ||||
|             AnyStateEvent::RoomAliases(StateEvent { | ||||
|                 content: AliasesEventContent { | ||||
|                     aliases, | ||||
|                 }), | ||||
|                 }, | ||||
|                 .. | ||||
|             } | ||||
|             }) | ||||
|         )) | ||||
|         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 ruma_events::{ | ||||
|     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 serde_json::{ | ||||
| @ -236,3 +237,48 @@ fn deserialize_avatar_without_prev_content() { | ||||
|             && 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.
 | ||||
|     name: AnyBasicEventContent, | ||||
|     name: AnyBasicEvent, | ||||
|     events: [ | ||||
|         "m.direct", | ||||
|         "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, | ||||
|     events: [ | ||||
|         "m.not.a.path", | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| event_content_enum! { | ||||
| event_enum! { | ||||
|     name: InvalidEvent, | ||||
|     events: [ | ||||
|         "not.a.path", | ||||
|  | ||||
| @ -1,15 +1,8 @@ | ||||
| error: proc macro panicked | ||||
|   --> $DIR/08-enum-invalid-path.rs:10:1 | ||||
| error: well-known matrix events have to start with `m.` found `not.a.path` | ||||
|   --> $DIR/08-enum-invalid-path.rs:13:9 | ||||
|    | | ||||
| 10 | / event_content_enum! { | ||||
| 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` | ||||
| 13 |         "not.a.path", | ||||
|    |         ^^^^^^^^^^^^ | ||||
| 
 | ||||
| error[E0433]: failed to resolve: could not find `not` in `ruma_events` | ||||
|  --> $DIR/08-enum-invalid-path.rs:6:9 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user