Add StateEvent<C> and AnyStateEvent
This commit is contained in:
parent
3d01bfa96d
commit
1a9b0f3e8b
@ -87,9 +87,10 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
let content = match &self.content {
|
||||
Content::Struct(fields) => {
|
||||
// TODO remove serde::Deserialize when this macro actually generates generic events
|
||||
quote! {
|
||||
#[doc = #content_docstring]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct #content_name {
|
||||
#(#fields),*
|
||||
}
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -119,7 +119,6 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use js_int::Int;
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// use self::room::redaction::RedactionEvent;
|
||||
@ -165,6 +164,7 @@ pub mod receipt;
|
||||
pub mod room;
|
||||
// pub mod room_key;
|
||||
pub mod room_key_request;
|
||||
pub mod state;
|
||||
pub mod sticker;
|
||||
// pub mod stripped;
|
||||
pub mod tag;
|
||||
@ -211,3 +211,23 @@ impl UnsignedData {
|
||||
// && self.redacted_because.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// The base trait that all event content types implement.
|
||||
///
|
||||
/// Implementing this trait allows content types to be serialized as well as deserialized.
|
||||
pub trait EventContent: Sized + Serialize {
|
||||
/// Constructs the given event content.
|
||||
fn from_parts(
|
||||
event_type: &str,
|
||||
content: &serde_json::value::RawValue,
|
||||
) -> Result<Self, InvalidEvent>;
|
||||
|
||||
/// A matrix event identifier, like `m.room.message`.
|
||||
fn event_type(&self) -> &str;
|
||||
}
|
||||
|
||||
/// Marker trait for room events.
|
||||
pub trait RoomEventContent: EventContent {}
|
||||
|
||||
/// Marker trait for state events.
|
||||
pub trait StateEventContent: RoomEventContent {}
|
||||
|
@ -2,6 +2,12 @@
|
||||
|
||||
use ruma_events_macros::ruma_event;
|
||||
use ruma_identifiers::RoomAliasId;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{
|
||||
error::{InvalidEvent, InvalidEventKind},
|
||||
EventContent, RoomEventContent, StateEventContent,
|
||||
};
|
||||
|
||||
ruma_event! {
|
||||
/// Informs the room about what room aliases it has been given.
|
||||
@ -14,3 +20,26 @@ ruma_event! {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl EventContent for AliasesEventContent {
|
||||
fn event_type(&self) -> &str {
|
||||
"m.room.aliases"
|
||||
}
|
||||
|
||||
fn from_parts(event_type: &str, content: &RawJsonValue) -> Result<Self, InvalidEvent> {
|
||||
if event_type != "m.room.aliases" {
|
||||
return Err(InvalidEvent {
|
||||
kind: InvalidEventKind::Deserialization,
|
||||
message: format!("expected `m.room.aliases` found {}", event_type),
|
||||
});
|
||||
}
|
||||
serde_json::from_str::<AliasesEventContent>(content.get()).map_err(|e| InvalidEvent {
|
||||
kind: InvalidEventKind::Deserialization,
|
||||
message: e.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomEventContent for AliasesEventContent {}
|
||||
|
||||
impl StateEventContent for AliasesEventContent {}
|
||||
|
@ -1,8 +1,13 @@
|
||||
//! Types for the *m.room.avatar* event.
|
||||
|
||||
use ruma_events_macros::ruma_event;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use super::ImageInfo;
|
||||
use crate::{
|
||||
error::{InvalidEvent, InvalidEventKind},
|
||||
EventContent, RoomEventContent, StateEventContent,
|
||||
};
|
||||
|
||||
ruma_event! {
|
||||
/// A picture that is associated with the room.
|
||||
@ -22,3 +27,26 @@ ruma_event! {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl EventContent for AvatarEventContent {
|
||||
fn event_type(&self) -> &str {
|
||||
"m.room.avatar"
|
||||
}
|
||||
|
||||
fn from_parts(event_type: &str, content: &RawJsonValue) -> Result<Self, InvalidEvent> {
|
||||
if event_type != "m.room.avatar" {
|
||||
return Err(InvalidEvent {
|
||||
kind: InvalidEventKind::Deserialization,
|
||||
message: format!("expected `m.room.avatar` found {}", event_type),
|
||||
});
|
||||
}
|
||||
serde_json::from_str::<AvatarEventContent>(content.get()).map_err(|e| InvalidEvent {
|
||||
kind: InvalidEventKind::Deserialization,
|
||||
message: e.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomEventContent for AvatarEventContent {}
|
||||
|
||||
impl StateEventContent for AvatarEventContent {}
|
||||
|
498
src/state.rs
Normal file
498
src/state.rs
Normal file
@ -0,0 +1,498 @@
|
||||
//! An enum that represents any state event. A state event is represented by
|
||||
//! a parameterized struct allowing more flexibility in whats being sent.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use serde::{
|
||||
de::{self, Deserialize, Deserializer, Error as _, MapAccess, Visitor},
|
||||
ser::{Error, SerializeStruct},
|
||||
Serialize, Serializer,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{
|
||||
error::{InvalidEvent, InvalidEventKind},
|
||||
room::{aliases::AliasesEventContent, avatar::AvatarEventContent},
|
||||
EventContent, RoomEventContent, StateEventContent,
|
||||
};
|
||||
|
||||
/// A state event.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum AnyStateEventContent {
|
||||
/// m.room.aliases
|
||||
RoomAliases(AliasesEventContent),
|
||||
|
||||
/// m.room.avatar
|
||||
RoomAvatar(AvatarEventContent),
|
||||
// /// m.room.canonical_alias
|
||||
// RoomCanonicalAlias(StateEvent<CanonicalAliasEventContent>),
|
||||
|
||||
// /// m.room.create
|
||||
// RoomCreate(StateEvent<CreateEventContent>),
|
||||
|
||||
// /// m.room.encryption
|
||||
// RoomEncryption(StateEvent<EncryptionEventContent>),
|
||||
|
||||
// /// m.room.guest_access
|
||||
// RoomGuestAccess(StateEvent<GuestAccessEventContent>),
|
||||
|
||||
// /// m.room.history_visibility
|
||||
// RoomHistoryVisibility(StateEvent<HistoryVisibilityEventContent>),
|
||||
|
||||
// /// m.room.join_rules
|
||||
// RoomJoinRules(StateEvent<JoinRulesEventContent>),
|
||||
|
||||
// /// m.room.member
|
||||
// RoomMember(StateEvent<MemberEventContent>),
|
||||
|
||||
// /// m.room.name
|
||||
// RoomName(StateEvent<NameEventContent>),
|
||||
|
||||
// /// m.room.pinned_events
|
||||
// RoomPinnedEvents(StateEvent<PinnedEventsEventContent>),
|
||||
|
||||
// /// m.room.power_levels
|
||||
// RoomPowerLevels(StateEvent<PinnedEventsEventContent>),
|
||||
|
||||
// /// m.room.server_acl
|
||||
// RoomServerAcl(StateEvent<ServerAclEventContent>),
|
||||
|
||||
// /// m.room.third_party_invite
|
||||
// RoomThirdPartyInvite(StateEvent<ThirdPartyInviteEventContent>),
|
||||
|
||||
// /// m.room.tombstone
|
||||
// RoomTombstone(StateEvent<TombstoneEventContent>),
|
||||
|
||||
// /// m.room.topic
|
||||
// RoomTopic(StateEvent<TopicEventContent>),
|
||||
|
||||
// /// Any state event that is not part of the specification.
|
||||
// CustomState(StateEvent<CustomEventContent>),
|
||||
}
|
||||
|
||||
/// To-device event.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StateEvent<C: StateEventContent> {
|
||||
/// Data specific to the event type.
|
||||
pub content: C,
|
||||
|
||||
/// The globally unique event identifier for the user who sent the event.
|
||||
pub event_id: EventId,
|
||||
|
||||
/// Contains the fully-qualified ID of the user who sent this event.
|
||||
pub sender: UserId,
|
||||
|
||||
/// Timestamp in milliseconds on originating homeserver when this event was sent.
|
||||
pub origin_server_ts: SystemTime,
|
||||
|
||||
/// The ID of the room associated with this event.
|
||||
pub room_id: RoomId,
|
||||
|
||||
/// A unique key which defines the overwriting semantics for this piece of room state.
|
||||
///
|
||||
/// This is often an empty string, but some events send a `UserId` to show
|
||||
/// which user the event affects.
|
||||
pub state_key: String,
|
||||
|
||||
/// Optional previous content for this event.
|
||||
pub prev_content: Option<C>,
|
||||
}
|
||||
|
||||
impl EventContent for AnyStateEventContent {
|
||||
fn event_type(&self) -> &str {
|
||||
match self {
|
||||
AnyStateEventContent::RoomAliases(content) => content.event_type(),
|
||||
AnyStateEventContent::RoomAvatar(content) => content.event_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(event_type: &str, content: &RawJsonValue) -> Result<Self, InvalidEvent> {
|
||||
fn deserialize_variant<T: StateEventContent>(
|
||||
ev_type: &str,
|
||||
input: &RawJsonValue,
|
||||
variant: fn(T) -> AnyStateEventContent,
|
||||
) -> Result<AnyStateEventContent, InvalidEvent> {
|
||||
let content = T::from_parts(ev_type, input)?;
|
||||
Ok(variant(content))
|
||||
}
|
||||
|
||||
match event_type {
|
||||
"m.room.avatar" => deserialize_variant::<AvatarEventContent>(
|
||||
event_type,
|
||||
content,
|
||||
AnyStateEventContent::RoomAvatar,
|
||||
),
|
||||
"m.room.aliases" => deserialize_variant::<AliasesEventContent>(
|
||||
event_type,
|
||||
content,
|
||||
AnyStateEventContent::RoomAliases,
|
||||
),
|
||||
ev => Err(InvalidEvent {
|
||||
kind: InvalidEventKind::Deserialization,
|
||||
message: format!("event not supported {}", ev),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomEventContent for AnyStateEventContent {}
|
||||
|
||||
impl StateEventContent for AnyStateEventContent {}
|
||||
|
||||
impl<C: StateEventContent> Serialize for StateEvent<C> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let event_type = self.content.event_type();
|
||||
|
||||
let time_since_epoch = self.origin_server_ts.duration_since(UNIX_EPOCH).unwrap();
|
||||
let timestamp = match UInt::try_from(time_since_epoch.as_millis()) {
|
||||
Ok(uint) => uint,
|
||||
Err(err) => return Err(S::Error::custom(err)),
|
||||
};
|
||||
|
||||
let mut state = serializer.serialize_struct("StateEvent", 7)?;
|
||||
state.serialize_field("content", &self.content)?;
|
||||
state.serialize_field("event_id", &self.event_id)?;
|
||||
state.serialize_field("sender", &self.sender)?;
|
||||
state.serialize_field("origin_server_ts", ×tamp)?;
|
||||
state.serialize_field("room_id", &self.room_id)?;
|
||||
state.serialize_field("state_key", &self.state_key)?;
|
||||
if let Some(content) = self.prev_content.as_ref() {
|
||||
state.serialize_field("prev_content", content)?;
|
||||
}
|
||||
state.serialize_field("type", event_type)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, C: StateEventContent> Deserialize<'de> for StateEvent<C> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(StateEventVisitor(std::marker::PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(field_identifier, rename_all = "snake_case")]
|
||||
enum Field {
|
||||
Type,
|
||||
Content,
|
||||
EventId,
|
||||
Sender,
|
||||
OriginServerTs,
|
||||
RoomId,
|
||||
StateKey,
|
||||
PrevContent,
|
||||
}
|
||||
|
||||
/// Visits the fields of a StateEvent<C> to handle deserialization of
|
||||
/// the `content` and `prev_content` fields.
|
||||
struct StateEventVisitor<C: StateEventContent>(PhantomData<C>);
|
||||
|
||||
impl<'de, C: StateEventContent> Visitor<'de> for StateEventVisitor<C> {
|
||||
type Value = StateEvent<C>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "struct implementing StateEventContent")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut content: Option<Box<RawJsonValue>> = None;
|
||||
let mut event_type: Option<String> = None;
|
||||
let mut event_id: Option<EventId> = None;
|
||||
let mut sender: Option<UserId> = None;
|
||||
let mut origin_server_ts: Option<UInt> = None;
|
||||
let mut room_id: Option<RoomId> = None;
|
||||
let mut state_key: Option<String> = None;
|
||||
let mut prev_content: Option<Box<RawJsonValue>> = None;
|
||||
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
Field::Content => {
|
||||
if content.is_some() {
|
||||
return Err(de::Error::duplicate_field("content"));
|
||||
}
|
||||
content = Some(map.next_value()?);
|
||||
}
|
||||
Field::EventId => {
|
||||
if event_id.is_some() {
|
||||
return Err(de::Error::duplicate_field("event_id"));
|
||||
}
|
||||
event_id = Some(map.next_value()?);
|
||||
}
|
||||
Field::Sender => {
|
||||
if sender.is_some() {
|
||||
return Err(de::Error::duplicate_field("sender"));
|
||||
}
|
||||
sender = Some(map.next_value()?);
|
||||
}
|
||||
Field::OriginServerTs => {
|
||||
if origin_server_ts.is_some() {
|
||||
return Err(de::Error::duplicate_field("origin_server_ts"));
|
||||
}
|
||||
origin_server_ts = Some(map.next_value()?);
|
||||
}
|
||||
Field::RoomId => {
|
||||
if room_id.is_some() {
|
||||
return Err(de::Error::duplicate_field("room_id"));
|
||||
}
|
||||
room_id = Some(map.next_value()?);
|
||||
}
|
||||
Field::StateKey => {
|
||||
if state_key.is_some() {
|
||||
return Err(de::Error::duplicate_field("state_key"));
|
||||
}
|
||||
state_key = Some(map.next_value()?);
|
||||
}
|
||||
Field::PrevContent => {
|
||||
if prev_content.is_some() {
|
||||
return Err(de::Error::duplicate_field("prev_content"));
|
||||
}
|
||||
prev_content = Some(map.next_value()?);
|
||||
}
|
||||
Field::Type => {
|
||||
if event_type.is_some() {
|
||||
return Err(de::Error::duplicate_field("type"));
|
||||
}
|
||||
event_type = Some(map.next_value()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let event_type = event_type.ok_or_else(|| de::Error::missing_field("type"))?;
|
||||
|
||||
let raw = content.ok_or_else(|| de::Error::missing_field("content"))?;
|
||||
let content = C::from_parts(&event_type, &raw).map_err(A::Error::custom)?;
|
||||
|
||||
let event_id = event_id.ok_or_else(|| de::Error::missing_field("event_id"))?;
|
||||
let sender = sender.ok_or_else(|| de::Error::missing_field("sender"))?;
|
||||
|
||||
let origin_server_ts = origin_server_ts
|
||||
.map(|time| UNIX_EPOCH + Duration::from_millis(time.into()))
|
||||
.ok_or_else(|| de::Error::missing_field("origin_server_ts"))?;
|
||||
|
||||
let room_id = room_id.ok_or_else(|| de::Error::missing_field("room_id"))?;
|
||||
let state_key = state_key.ok_or_else(|| de::Error::missing_field("state_key"))?;
|
||||
|
||||
let prev_content = if let Some(raw) = prev_content {
|
||||
Some(C::from_parts(&event_type, &raw).map_err(A::Error::custom)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(StateEvent {
|
||||
content,
|
||||
event_id,
|
||||
sender,
|
||||
origin_server_ts,
|
||||
room_id,
|
||||
state_key,
|
||||
prev_content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use js_int::UInt;
|
||||
use matches::assert_matches;
|
||||
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::{AliasesEventContent, AnyStateEventContent, AvatarEventContent, StateEvent};
|
||||
use crate::room::{ImageInfo, ThumbnailInfo};
|
||||
|
||||
#[test]
|
||||
fn serialize_aliases_with_prev_content() {
|
||||
let aliases_event = StateEvent {
|
||||
content: AnyStateEventContent::RoomAliases(AliasesEventContent {
|
||||
aliases: vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()],
|
||||
}),
|
||||
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
|
||||
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
|
||||
prev_content: Some(AnyStateEventContent::RoomAliases(AliasesEventContent {
|
||||
aliases: vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()],
|
||||
})),
|
||||
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
|
||||
sender: UserId::try_from("@carl:example.com").unwrap(),
|
||||
state_key: "".to_string(),
|
||||
};
|
||||
|
||||
let actual = to_json_value(&aliases_event).unwrap();
|
||||
let expected = json!({
|
||||
"content": {
|
||||
"aliases": [ "#somewhere:localhost" ]
|
||||
},
|
||||
"event_id": "$h29iv0s8:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"prev_content": {
|
||||
"aliases": [ "#somewhere:localhost" ]
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.aliases",
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_aliases_without_prev_content() {
|
||||
let aliases_event = StateEvent {
|
||||
content: AnyStateEventContent::RoomAliases(AliasesEventContent {
|
||||
aliases: vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()],
|
||||
}),
|
||||
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
|
||||
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
|
||||
prev_content: None,
|
||||
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
|
||||
sender: UserId::try_from("@carl:example.com").unwrap(),
|
||||
state_key: "".to_string(),
|
||||
};
|
||||
|
||||
let actual = to_json_value(&aliases_event).unwrap();
|
||||
let expected = json!({
|
||||
"content": {
|
||||
"aliases": [ "#somewhere:localhost" ]
|
||||
},
|
||||
"event_id": "$h29iv0s8:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"room_id": "!roomid:room.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.aliases",
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_aliases_with_prev_content() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"aliases": [ "#somewhere:localhost" ]
|
||||
},
|
||||
"event_id": "$h29iv0s8:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"prev_content": {
|
||||
"aliases": [ "#inner:localhost" ]
|
||||
},
|
||||
"room_id": "!roomid:room.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.aliases"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<StateEvent<AnyStateEventContent>>(json_data).unwrap(),
|
||||
StateEvent {
|
||||
content: AnyStateEventContent::RoomAliases(content),
|
||||
event_id,
|
||||
origin_server_ts,
|
||||
prev_content: Some(AnyStateEventContent::RoomAliases(prev_content)),
|
||||
room_id,
|
||||
sender,
|
||||
state_key,
|
||||
} if content.aliases == vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()]
|
||||
&& event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
|
||||
&& origin_server_ts == UNIX_EPOCH + Duration::from_millis(1)
|
||||
&& prev_content.aliases == vec![RoomAliasId::try_from("#inner:localhost").unwrap()]
|
||||
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
|
||||
&& sender == UserId::try_from("@carl:example.com").unwrap()
|
||||
&& state_key == ""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_avatar_without_prev_content() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"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",
|
||||
"state_key": "",
|
||||
"type": "m.room.avatar"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<StateEvent<AnyStateEventContent>>(json_data).unwrap(),
|
||||
StateEvent {
|
||||
content: AnyStateEventContent::RoomAvatar(AvatarEventContent {
|
||||
info: Some(ImageInfo {
|
||||
height,
|
||||
width,
|
||||
mimetype: Some(mimetype),
|
||||
size,
|
||||
thumbnail_info: Some(ThumbnailInfo {
|
||||
width: thumb_width,
|
||||
height: thumb_height,
|
||||
mimetype: thumb_mimetype,
|
||||
size: thumb_size,
|
||||
}),
|
||||
thumbnail_url: Some(thumbnail_url),
|
||||
thumbnail_file: None,
|
||||
}),
|
||||
url,
|
||||
}),
|
||||
event_id,
|
||||
origin_server_ts,
|
||||
prev_content: None,
|
||||
room_id,
|
||||
sender,
|
||||
state_key,
|
||||
} if 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()
|
||||
&& state_key == ""
|
||||
&& height == UInt::new(423)
|
||||
&& width == UInt::new(1011)
|
||||
&& mimetype == "image/png"
|
||||
&& size == UInt::new(84242)
|
||||
&& thumb_width == UInt::new(800)
|
||||
&& thumb_height == UInt::new(334)
|
||||
&& thumb_mimetype == Some("image/png".to_string())
|
||||
&& thumb_size == UInt::new(82595)
|
||||
&& thumbnail_url == "mxc://matrix.org"
|
||||
&& url == "http://www.matrix.org"
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user