Implement PresenceEvent and EphemeralEvent
* Add derive for PresenceEventContent and create struct AnyPresenceEventContent since there is only one content type * Add derive for Ephemeral event and create enum AnyEphemeralEventContent, convert receipt and typing to use derive(EphemeralEventContent) over ruma_api!
This commit is contained in:
parent
ef3a6787a0
commit
800fba7c32
@ -16,6 +16,9 @@ fn marker_traits(ident: &Ident) -> TokenStream {
|
||||
impl ::ruma_events::RoomEventContent for #ident {}
|
||||
impl ::ruma_events::MessageEventContent for #ident {}
|
||||
},
|
||||
"AnyEphemeralRoomEventContent" => quote! {
|
||||
impl ::ruma_events::EphemeralRoomEventContent for #ident {}
|
||||
},
|
||||
_ => TokenStream::new(),
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ use syn::{Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident};
|
||||
/// Derive `Event` macro code generation.
|
||||
pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = &input.ident;
|
||||
let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
|
||||
let is_presence_event = ident == "PresenceEvent";
|
||||
|
||||
let fields = if let Data::Struct(DataStruct { fields, .. }) = input.data.clone() {
|
||||
if let Fields::Named(FieldsNamed { named, .. }) = fields {
|
||||
if !named.iter().any(|f| f.ident.as_ref().unwrap() == "content") {
|
||||
@ -30,8 +33,6 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
));
|
||||
};
|
||||
|
||||
let content_trait = Ident::new(&format!("{}Content", ident), input.ident.span());
|
||||
|
||||
let serialize_fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
@ -66,20 +67,25 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let event_ty = if is_presence_event {
|
||||
quote! {
|
||||
"m.presence";
|
||||
}
|
||||
} else {
|
||||
quote! { self.content.event_type(); }
|
||||
};
|
||||
|
||||
let serialize_impl = quote! {
|
||||
impl<C> ::serde::ser::Serialize for #ident<C>
|
||||
where
|
||||
C: ::ruma_events::#content_trait,
|
||||
{
|
||||
impl #impl_gen ::serde::ser::Serialize for #ident #ty_gen #where_clause {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::ser::Serializer,
|
||||
{
|
||||
use ::serde::ser::SerializeStruct as _;
|
||||
|
||||
let event_type = self.content.event_type();
|
||||
let event_type = #event_ty;
|
||||
|
||||
let mut state = serializer.serialize_struct("StateEvent", 7)?;
|
||||
let mut state = serializer.serialize_struct(stringify!(#ident), 7)?;
|
||||
|
||||
state.serialize_field("type", event_type)?;
|
||||
#( #serialize_fields )*
|
||||
@ -88,7 +94,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
}
|
||||
};
|
||||
|
||||
let deserialize_impl = expand_deserialize_event(&input, fields)?;
|
||||
let deserialize_impl = expand_deserialize_event(is_presence_event, input, fields)?;
|
||||
|
||||
Ok(quote! {
|
||||
#serialize_impl
|
||||
@ -97,9 +103,14 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Result<TokenStream> {
|
||||
fn expand_deserialize_event(
|
||||
is_presence_event: bool,
|
||||
input: DeriveInput,
|
||||
fields: Vec<Field>,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let ident = &input.ident;
|
||||
let content_ident = Ident::new(&format!("{}Content", ident), input.ident.span());
|
||||
let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let enum_variants = fields
|
||||
.iter()
|
||||
@ -115,7 +126,11 @@ fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Res
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
let ty = &field.ty;
|
||||
if name == "content" || name == "prev_content" {
|
||||
if is_presence_event {
|
||||
quote! { #content_ident }
|
||||
} else {
|
||||
quote! { Box<::serde_json::value::RawValue> }
|
||||
}
|
||||
} else if name == "origin_server_ts" {
|
||||
quote! { ::js_int::UInt }
|
||||
} else {
|
||||
@ -129,10 +144,16 @@ fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Res
|
||||
.map(|field| {
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
if name == "content" {
|
||||
if is_presence_event {
|
||||
quote! {
|
||||
let content = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
|
||||
let content = C::from_parts(&event_type, json).map_err(A::Error::custom)?;
|
||||
}
|
||||
}
|
||||
} else if name == "prev_content" {
|
||||
quote! {
|
||||
let prev_content = if let Some(json) = prev_content {
|
||||
@ -164,11 +185,20 @@ fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Res
|
||||
|
||||
let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();
|
||||
|
||||
let deserialize_impl_gen = if is_presence_event {
|
||||
quote! { <'de> }
|
||||
} else {
|
||||
let gen = &input.generics.params;
|
||||
quote! { <'de, #gen> }
|
||||
};
|
||||
let deserialize_phantom_type = if is_presence_event {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { ::std::marker::PhantomData }
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
impl<'de, C> ::serde::de::Deserialize<'de> for #ident<C>
|
||||
where
|
||||
C: ::ruma_events::#content_ident,
|
||||
{
|
||||
impl #deserialize_impl_gen ::serde::de::Deserialize<'de> for #ident #ty_gen #where_clause {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: ::serde::de::Deserializer<'de>,
|
||||
@ -183,13 +213,10 @@ fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Res
|
||||
|
||||
/// Visits the fields of an event struct to handle deserialization of
|
||||
/// the `content` and `prev_content` fields.
|
||||
struct EventVisitor<C>(::std::marker::PhantomData<C>);
|
||||
struct EventVisitor #impl_generics (#deserialize_phantom_type #ty_gen);
|
||||
|
||||
impl<'de, C> ::serde::de::Visitor<'de> for EventVisitor<C>
|
||||
where
|
||||
C: ::ruma_events::#content_ident,
|
||||
{
|
||||
type Value = #ident<C>;
|
||||
impl #deserialize_impl_gen ::serde::de::Visitor<'de> for EventVisitor #ty_gen #where_clause {
|
||||
type Value = #ident #ty_gen;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(formatter, "struct implementing {}", stringify!(#content_ident))
|
||||
@ -232,7 +259,7 @@ fn expand_deserialize_event(input: &DeriveInput, fields: Vec<Field>) -> syn::Res
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(EventVisitor(::std::marker::PhantomData))
|
||||
deserializer.deserialize_map(EventVisitor(#deserialize_phantom_type))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -23,10 +23,7 @@ impl Parse for EventMeta {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `RoomEventContent` implementation for a struct.
|
||||
///
|
||||
/// This is used internally for code sharing as `RoomEventContent` is not derivable.
|
||||
fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
fn expand_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = &input.ident;
|
||||
|
||||
let event_type_attr = input
|
||||
@ -47,7 +44,7 @@ fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
lit
|
||||
};
|
||||
|
||||
let event_content_impl = quote! {
|
||||
Ok(quote! {
|
||||
impl ::ruma_events::EventContent for #ident {
|
||||
fn event_type(&self) -> &str {
|
||||
#event_type
|
||||
@ -64,7 +61,15 @@ fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
::serde_json::from_str(content.get()).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `RoomEventContent` implementation for a struct.
|
||||
///
|
||||
/// This is used internally for code sharing as `RoomEventContent` is not derivable.
|
||||
fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = input.ident.clone();
|
||||
let event_content_impl = expand_event_content(input)?;
|
||||
|
||||
Ok(quote! {
|
||||
#event_content_impl
|
||||
@ -85,7 +90,7 @@ pub fn expand_message_event_content(input: DeriveInput) -> syn::Result<TokenStre
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `MessageEventContent` implementation for a struct
|
||||
/// Create a `StateEventContent` implementation for a struct
|
||||
pub fn expand_state_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = input.ident.clone();
|
||||
let room_ev_content = expand_room_event_content(input)?;
|
||||
@ -96,3 +101,27 @@ pub fn expand_state_event_content(input: DeriveInput) -> syn::Result<TokenStream
|
||||
impl ::ruma_events::StateEventContent for #ident { }
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `PresenceEventContent` implementation for a struct
|
||||
pub fn expand_presence_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = input.ident.clone();
|
||||
let event_content_impl = expand_event_content(input)?;
|
||||
|
||||
Ok(quote! {
|
||||
#event_content_impl
|
||||
|
||||
impl ::ruma_events::PresenceEventContent for #ident { }
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `EphemeralRoomEventContent` implementation for a struct
|
||||
pub fn expand_ephemeral_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = input.ident.clone();
|
||||
let event_content_impl = expand_event_content(input)?;
|
||||
|
||||
Ok(quote! {
|
||||
#event_content_impl
|
||||
|
||||
impl ::ruma_events::EphemeralRoomEventContent for #ident { }
|
||||
})
|
||||
}
|
||||
|
@ -17,7 +17,10 @@ use syn::{parse_macro_input, DeriveInput};
|
||||
use self::{
|
||||
content_enum::{expand_content_enum, parse::ContentEnumInput},
|
||||
event::expand_event,
|
||||
event_content::{expand_message_event_content, expand_state_event_content},
|
||||
event_content::{
|
||||
expand_ephemeral_event_content, expand_message_event_content,
|
||||
expand_presence_event_content, expand_state_event_content,
|
||||
},
|
||||
gen::RumaEvent,
|
||||
parse::RumaEventInput,
|
||||
};
|
||||
@ -152,6 +155,24 @@ pub fn derive_state_event_content(input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates an implementation of `ruma_events::PresenceEventContent` and it's super traits.
|
||||
#[proc_macro_derive(PresenceEventContent, attributes(ruma_event))]
|
||||
pub fn derive_presence_event_content(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
expand_presence_event_content(input)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates an implementation of `ruma_events::EphemeralRoomEventContent` and it's super traits.
|
||||
#[proc_macro_derive(EphemeralRoomEventContent, attributes(ruma_event))]
|
||||
pub fn derive_ephemeral_event_content(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
expand_ephemeral_event_content(input)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Generates implementations needed to serialize and deserialize Matrix events.
|
||||
#[proc_macro_derive(Event, attributes(ruma_event))]
|
||||
pub fn derive_state_event(input: TokenStream) -> TokenStream {
|
||||
|
@ -34,3 +34,9 @@ event_content_enum! {
|
||||
"m.room.topic",
|
||||
]
|
||||
}
|
||||
|
||||
event_content_enum! {
|
||||
/// An ephemeral room event.
|
||||
name: AnyEphemeralRoomEventContent,
|
||||
events: [ "m.typing", "m.receipt" ]
|
||||
}
|
||||
|
@ -1,18 +1,11 @@
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use std::{convert::TryFrom, time::SystemTime};
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_events_macros::Event;
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use serde::{
|
||||
ser::{Error, SerializeStruct},
|
||||
Serialize, Serializer,
|
||||
};
|
||||
use serde::ser::Error;
|
||||
|
||||
use crate::{
|
||||
BasicEventContent, MessageEventContent, RoomEventContent, StateEventContent,
|
||||
BasicEventContent, EphemeralRoomEventContent, MessageEventContent, StateEventContent,
|
||||
ToDeviceEventContent, UnsignedData,
|
||||
};
|
||||
|
||||
@ -22,6 +15,16 @@ pub struct BasicEvent<C: BasicEventContent> {
|
||||
pub content: C,
|
||||
}
|
||||
|
||||
/// Ephemeral room event.
|
||||
#[derive(Clone, Debug, Event)]
|
||||
pub struct EphemeralRoomEvent<C: EphemeralRoomEventContent> {
|
||||
/// Data specific to the event type.
|
||||
pub content: C,
|
||||
|
||||
/// The ID of the room associated with this event.
|
||||
pub room_id: RoomId,
|
||||
}
|
||||
|
||||
/// Message event.
|
||||
#[derive(Clone, Debug, Event)]
|
||||
pub struct MessageEvent<C: MessageEventContent> {
|
||||
|
@ -151,7 +151,7 @@ pub mod forwarded_room_key;
|
||||
pub mod fully_read;
|
||||
// pub mod ignored_user_list;
|
||||
pub mod key;
|
||||
// pub mod presence;
|
||||
pub mod presence;
|
||||
// pub mod push_rules;
|
||||
pub mod receipt;
|
||||
pub mod room;
|
||||
@ -165,10 +165,10 @@ pub mod typing;
|
||||
|
||||
pub use self::{
|
||||
algorithm::Algorithm,
|
||||
content_enums::{AnyMessageEventContent, AnyStateEventContent},
|
||||
content_enums::{AnyEphemeralRoomEventContent, AnyMessageEventContent, AnyStateEventContent},
|
||||
error::{FromStrError, InvalidEvent, InvalidInput},
|
||||
event_enums::AnyStateEvent,
|
||||
event_kinds::{MessageEvent, StateEvent},
|
||||
event_kinds::{EphemeralRoomEvent, MessageEvent, StateEvent},
|
||||
event_type::EventType,
|
||||
json::EventJson,
|
||||
};
|
||||
@ -217,6 +217,9 @@ pub trait EventContent: Sized + Serialize {
|
||||
fn from_parts(event_type: &str, content: Box<RawJsonValue>) -> Result<Self, String>;
|
||||
}
|
||||
|
||||
/// Marker trait for the content of an ephemeral room event.
|
||||
pub trait EphemeralRoomEventContent: EventContent {}
|
||||
|
||||
/// Marker trait for the content of a basic event.
|
||||
pub trait BasicEventContent: EventContent {}
|
||||
|
||||
|
@ -1,19 +1,30 @@
|
||||
//! Types for the *m.presence* event.
|
||||
//! A presence event is represented by a parameterized struct.
|
||||
//!
|
||||
//! There is only one type that will satisfy the bounds of `PresenceEventContent`
|
||||
//! as this event has only one possible content value according to Matrix spec.
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_events_macros::ruma_event;
|
||||
pub use ruma_common::presence::PresenceState;
|
||||
use ruma_events_macros::Event;
|
||||
use ruma_identifiers::UserId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
ruma_event! {
|
||||
/// Informs the client of a user's presence state change.
|
||||
PresenceEvent {
|
||||
kind: Event,
|
||||
event_type: "m.presence",
|
||||
fields: {
|
||||
/// The unique identifier for the user associated with this event.
|
||||
/// Presence event.
|
||||
#[derive(Clone, Debug, Event)]
|
||||
pub struct PresenceEvent {
|
||||
/// Data specific to the event type.
|
||||
pub content: PresenceEventContent,
|
||||
|
||||
/// Contains the fully-qualified ID of the user who sent this event.
|
||||
pub sender: UserId,
|
||||
},
|
||||
content: {
|
||||
}
|
||||
|
||||
/// Informs the room of members presence.
|
||||
///
|
||||
/// This is the only event content a `PresenceEvent` can contain as it's
|
||||
/// `content` field.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PresenceEventContent {
|
||||
/// The current avatar URL for this user.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar_url: Option<String>,
|
||||
@ -36,12 +47,8 @@ ruma_event! {
|
||||
/// An optional description to accompany the presence.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status_msg: Option<String>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub use ruma_common::presence::PresenceState;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryFrom;
|
||||
@ -51,7 +58,7 @@ mod tests {
|
||||
use ruma_identifiers::UserId;
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::{PresenceEventContent, PresenceState};
|
||||
use super::{PresenceEvent, PresenceEventContent, PresenceState};
|
||||
use crate::EventJson;
|
||||
|
||||
#[test]
|
||||
|
@ -2,30 +2,20 @@
|
||||
|
||||
use std::{collections::BTreeMap, time::SystemTime};
|
||||
|
||||
use ruma_events_macros::ruma_event;
|
||||
use ruma_events_macros::EphemeralRoomEventContent;
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
ruma_event! {
|
||||
/// Informs the client of new receipts.
|
||||
ReceiptEvent {
|
||||
kind: Event,
|
||||
event_type: "m.receipt",
|
||||
fields: {
|
||||
/// The unique identifier for the room associated with this event.
|
||||
///
|
||||
/// `None` if the room is known through other means (such as this even being part of an
|
||||
/// event list scoped to a room in a `/sync` response)
|
||||
pub room_id: Option<RoomId>,
|
||||
},
|
||||
content_type_alias: {
|
||||
/// Informs the client who has read a message specified by it's event id.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EphemeralRoomEventContent)]
|
||||
#[ruma_event(type = "m.receipt")]
|
||||
#[serde(transparent)]
|
||||
pub struct ReceiptEventContent {
|
||||
/// The payload for `ReceiptEvent`.
|
||||
///
|
||||
/// A mapping of event ID to a collection of receipts for this event ID. The event ID is the ID of
|
||||
/// the event being acknowledged and *not* an ID for the receipt itself.
|
||||
BTreeMap<EventId, Receipts>
|
||||
},
|
||||
}
|
||||
pub receipts: BTreeMap<EventId, Receipts>,
|
||||
}
|
||||
|
||||
/// A collection of receipts.
|
||||
|
@ -1,23 +1,13 @@
|
||||
//! Types for the *m.typing* event.
|
||||
|
||||
use ruma_events_macros::ruma_event;
|
||||
use ruma_events_macros::EphemeralRoomEventContent;
|
||||
use ruma_identifiers::{RoomId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
ruma_event! {
|
||||
/// Informs the client of the list of users currently typing.
|
||||
TypingEvent {
|
||||
kind: Event,
|
||||
event_type: "m.typing",
|
||||
fields: {
|
||||
/// The unique identifier for the room associated with this event.
|
||||
///
|
||||
/// `None` if the room is known through other means (such as this even being part of an
|
||||
/// event list scoped to a room in a `/sync` response)
|
||||
pub room_id: Option<RoomId>,
|
||||
},
|
||||
content: {
|
||||
/// Informs the client who is currently typing in a given room.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EphemeralRoomEventContent)]
|
||||
#[ruma_event(type = "m.typing")]
|
||||
pub struct TypingEventContent {
|
||||
/// The list of user IDs typing in this room, if any.
|
||||
pub user_ids: Vec<UserId>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
133
tests/ephemeral_event.rs
Normal file
133
tests/ephemeral_event.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use maplit::btreemap;
|
||||
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::{
|
||||
receipt::{Receipt, ReceiptEventContent, Receipts},
|
||||
typing::TypingEventContent,
|
||||
AnyEphemeralRoomEventContent, EphemeralRoomEvent, EventJson,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn ephemeral_serialize_typing() {
|
||||
let aliases_event = EphemeralRoomEvent {
|
||||
content: AnyEphemeralRoomEventContent::Typing(TypingEventContent {
|
||||
user_ids: vec![UserId::try_from("@carl:example.com").unwrap()],
|
||||
}),
|
||||
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
|
||||
};
|
||||
|
||||
let actual = to_json_value(&aliases_event).unwrap();
|
||||
let expected = json!({
|
||||
"content": {
|
||||
"user_ids": [ "@carl:example.com" ]
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"type": "m.typing",
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_ephemeral_typing() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"user_ids": [ "@carl:example.com" ]
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"type": "m.typing"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<EventJson<EphemeralRoomEvent<AnyEphemeralRoomEventContent>>>(json_data)
|
||||
.unwrap()
|
||||
.deserialize()
|
||||
.unwrap(),
|
||||
EphemeralRoomEvent {
|
||||
content: AnyEphemeralRoomEventContent::Typing(TypingEventContent {
|
||||
user_ids,
|
||||
}),
|
||||
room_id,
|
||||
} if user_ids[0] == UserId::try_from("@carl:example.com").unwrap()
|
||||
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ephemeral_serialize_receipt() {
|
||||
let event_id = EventId::try_from("$h29iv0s8:example.com").unwrap();
|
||||
let user_id = UserId::try_from("@carl:example.com").unwrap();
|
||||
|
||||
let aliases_event = EphemeralRoomEvent {
|
||||
content: AnyEphemeralRoomEventContent::Receipt(ReceiptEventContent {
|
||||
receipts: btreemap! {
|
||||
event_id => Receipts {
|
||||
read: Some(btreemap! {
|
||||
user_id => Receipt { ts: Some(UNIX_EPOCH + Duration::from_millis(1)) },
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
|
||||
};
|
||||
|
||||
let actual = to_json_value(&aliases_event).unwrap();
|
||||
let expected = json!({
|
||||
"content": {
|
||||
"$h29iv0s8:example.com": {
|
||||
"m.read": {
|
||||
"@carl:example.com": { "ts": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"type": "m.receipt"
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_ephemeral_receipt() {
|
||||
let event_id = EventId::try_from("$h29iv0s8:example.com").unwrap();
|
||||
let user_id = UserId::try_from("@carl:example.com").unwrap();
|
||||
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"$h29iv0s8:example.com": {
|
||||
"m.read": {
|
||||
"@carl:example.com": { "ts": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"type": "m.receipt"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<EventJson<EphemeralRoomEvent<AnyEphemeralRoomEventContent>>>(json_data)
|
||||
.unwrap()
|
||||
.deserialize()
|
||||
.unwrap(),
|
||||
EphemeralRoomEvent {
|
||||
content: AnyEphemeralRoomEventContent::Receipt(ReceiptEventContent {
|
||||
receipts,
|
||||
}),
|
||||
room_id,
|
||||
} if !receipts.is_empty() && receipts.contains_key(&event_id)
|
||||
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
|
||||
&& receipts
|
||||
.get(&event_id)
|
||||
.map(|r| r.read.as_ref().unwrap().get(&user_id).unwrap().clone())
|
||||
.map(|r| r.ts)
|
||||
.unwrap()
|
||||
== Some(UNIX_EPOCH + Duration::from_millis(1))
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user