From 5091b9a9a855b56253bff9885461369202a24726 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sun, 31 May 2020 17:29:22 +0200 Subject: [PATCH] Integrate generic event types and EventContent trait with TryFromRaw --- ruma-events-macros/src/collection.rs | 4 +- ruma-events-macros/src/event_content.rs | 11 +- src/from_raw.rs | 4 +- src/json.rs | 35 +- src/lib.rs | 34 +- src/room.rs | 2 +- src/room/member.rs | 108 +++--- src/state.rs | 456 ++++++++++++++++-------- 8 files changed, 420 insertions(+), 234 deletions(-) diff --git a/ruma-events-macros/src/collection.rs b/ruma-events-macros/src/collection.rs index 94c90244..05d62a9b 100644 --- a/ruma-events-macros/src/collection.rs +++ b/ruma-events-macros/src/collection.rs @@ -28,8 +28,8 @@ pub fn expand_collection(input: RumaCollectionInput) -> syn::Result let collection = quote! { #( #attrs )* - #[derive(Clone, Debug, Serialize)] - #[serde(untagged)] + #[derive(Clone, Debug, /*Serialize*/)] + //#[serde(untagged)] #[allow(clippy::large_enum_variant)] pub enum #ident { #( #variants ),* diff --git a/ruma-events-macros/src/event_content.rs b/ruma-events-macros/src/event_content.rs index 223f677b..79d60247 100644 --- a/ruma-events-macros/src/event_content.rs +++ b/ruma-events-macros/src/event_content.rs @@ -52,19 +52,18 @@ fn expand_room_event(input: DeriveInput) -> syn::Result { fn event_type(&self) -> &str { #event_type } + } + impl ::ruma_events::RawEventContent for raw::#ident { fn from_parts( ev_type: &str, content: Box<::serde_json::value::RawValue> - ) -> Result { + ) -> Result { if ev_type != #event_type { - return Err( - ::ruma_events::InvalidEvent::wrong_event_type(#event_type, ev_type) - ); + return Err(format!("expected `{}` found {}", #event_type, ev_type)); } - let ev_json = ::ruma_events::EventJson::from(content); - ev_json.deserialize() + ::serde_json::from_str(content.get()).map_err(|e| e.to_string()) } } }; diff --git a/src/from_raw.rs b/src/from_raw.rs index 631ab934..2a94ba50 100644 --- a/src/from_raw.rs +++ b/src/from_raw.rs @@ -9,7 +9,7 @@ use serde::de::DeserializeOwned; /// [try]: trait.TryFromRaw.html pub trait FromRaw: Sized { /// The raw type. - type Raw: DeserializeOwned; + type Raw; /// Converts the raw type to `Self`. fn from_raw(_: Self::Raw) -> Self; @@ -19,7 +19,7 @@ pub trait FromRaw: Sized { /// corresponding 'raw' type, a potentially invalid representation that can be converted to `Self`. pub trait TryFromRaw: Sized { /// The raw type. - type Raw: DeserializeOwned; + type Raw; /// The error type returned if conversion fails. type Err: Display; diff --git a/src/json.rs b/src/json.rs index 8a433959..648d5e0f 100644 --- a/src/json.rs +++ b/src/json.rs @@ -5,14 +5,14 @@ use std::{ }; use serde::{ - de::{Deserialize, Deserializer}, + de::{Deserialize, DeserializeOwned, Deserializer}, ser::{Serialize, Serializer}, }; use serde_json::value::RawValue; use crate::{ error::{InvalidEvent, InvalidEventKind}, - TryFromRaw, + EventContent, RawEventContent, TryFromRaw, }; /// A wrapper around `Box`, to be used in place of event [content] [collection] types in @@ -42,7 +42,10 @@ impl EventJson { } } -impl EventJson { +impl EventJson +where + T::Raw: DeserializeOwned, +{ /// Try to deserialize the JSON into the expected event type. pub fn deserialize(&self) -> Result { let raw_ev: T::Raw = match serde_json::from_str(self.json.get()) { @@ -65,6 +68,32 @@ impl EventJson { } } +impl EventJson +where + T::Raw: RawEventContent, +{ + /// Try to deserialize the JSON as event content + pub fn deserialize_content(self, event_type: &str) -> Result { + let raw_content = match T::Raw::from_parts(event_type, self.json) { + Ok(raw) => raw, + Err(message) => { + return Err(InvalidEvent { + message, + kind: InvalidEventKind::Deserialization, + }); + } + }; + + match T::try_from_raw(raw_content) { + Ok(value) => Ok(value), + Err(err) => Err(InvalidEvent { + message: err.to_string(), + kind: InvalidEventKind::Validation, + }), + } + } +} + impl From<&T> for EventJson { fn from(val: &T) -> Self { Self::new(serde_json::value::to_raw_value(val).unwrap()) diff --git a/src/lib.rs b/src/lib.rs index e9c46d69..4e8e5704 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,7 +168,7 @@ pub use self::{ event_type::EventType, from_raw::{FromRaw, TryFromRaw}, json::EventJson, - state::{AnyStateEventContent, StateEvent}, + state::StateEvent, }; /// Extra information about an event that is not incorporated into the event's @@ -207,19 +207,37 @@ impl UnsignedData { /// 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: Box) -> Result; - +pub trait EventContent: TryFromRaw + Serialize +where + Self::Raw: RawEventContent, +{ /// A matrix event identifier, like `m.room.message`. fn event_type(&self) -> &str; } +#[doc(hidden)] +pub trait RawEventContent: Sized { + /// Constructs the given event content. + fn from_parts(event_type: &str, content: Box) -> Result; +} + /// Marker trait for the content of a room event. -pub trait RoomEventContent: EventContent {} +pub trait RoomEventContent: EventContent +where + Self::Raw: RawEventContent, +{ +} /// Marker trait for the content of a message event. -pub trait MessageEventContent: RoomEventContent {} +pub trait MessageEventContent: RoomEventContent +where + Self::Raw: RawEventContent, +{ +} /// Marker trait for the content of a state event. -pub trait StateEventContent: RoomEventContent {} +pub trait StateEventContent: RoomEventContent +where + Self::Raw: RawEventContent, +{ +} diff --git a/src/room.rs b/src/room.rs index d15ab58c..dc115e74 100644 --- a/src/room.rs +++ b/src/room.rs @@ -16,7 +16,7 @@ pub mod encryption; pub mod guest_access; pub mod history_visibility; pub mod join_rules; -// pub mod member; +pub mod member; // pub mod message; // pub mod name; // pub mod pinned_events; diff --git a/src/room/member.rs b/src/room/member.rs index b9052780..7ff20993 100644 --- a/src/room/member.rs +++ b/src/room/member.rs @@ -7,6 +7,8 @@ use ruma_identifiers::UserId; use serde::{Deserialize, Serialize}; use strum::{Display, EnumString}; +use crate::StateEvent; + /// The current membership state of a user in the room. /// /// Adjusts the membership state for a user in a room. It is preferable to use the membership @@ -150,44 +152,44 @@ pub enum MembershipChange { NotImplemented, } -// impl MemberEvent { -// /// Helper function for membership change. Check [the specification][spec] for details. -// /// -// /// [spec]: https://matrix.org/docs/spec/client_server/latest#m-room-member -// pub fn membership_change(&self) -> MembershipChange { -// use MembershipState::*; -// let prev_membership = if let Some(prev_content) = &self.prev_content { -// prev_content.membership -// } else { -// Leave -// }; -// match (prev_membership, &self.content.membership) { -// (Invite, Invite) | (Leave, Leave) | (Ban, Ban) => MembershipChange::None, -// (Invite, Join) | (Leave, Join) => MembershipChange::Joined, -// (Invite, Leave) => { -// if self.sender == self.state_key { -// MembershipChange::InvitationRevoked -// } else { -// MembershipChange::InvitationRejected -// } -// } -// (Invite, Ban) | (Leave, Ban) => MembershipChange::Banned, -// (Join, Invite) | (Ban, Invite) | (Ban, Join) => MembershipChange::Error, -// (Join, Join) => MembershipChange::ProfileChanged, -// (Join, Leave) => { -// if self.sender == self.state_key { -// MembershipChange::Left -// } else { -// MembershipChange::Kicked -// } -// } -// (Join, Ban) => MembershipChange::KickedAndBanned, -// (Leave, Invite) => MembershipChange::Invited, -// (Ban, Leave) => MembershipChange::Unbanned, -// (Knock, _) | (_, Knock) => MembershipChange::NotImplemented, -// } -// } -// } +impl StateEvent { + /// Helper function for membership change. Check [the specification][spec] for details. + /// + /// [spec]: https://matrix.org/docs/spec/client_server/latest#m-room-member + pub fn membership_change(&self) -> MembershipChange { + use MembershipState::*; + let prev_membership = if let Some(prev_content) = &self.prev_content { + prev_content.membership + } else { + Leave + }; + match (prev_membership, &self.content.membership) { + (Invite, Invite) | (Leave, Leave) | (Ban, Ban) => MembershipChange::None, + (Invite, Join) | (Leave, Join) => MembershipChange::Joined, + (Invite, Leave) => { + if self.sender == self.state_key { + MembershipChange::InvitationRevoked + } else { + MembershipChange::InvitationRejected + } + } + (Invite, Ban) | (Leave, Ban) => MembershipChange::Banned, + (Join, Invite) | (Ban, Invite) | (Ban, Join) => MembershipChange::Error, + (Join, Join) => MembershipChange::ProfileChanged, + (Join, Leave) => { + if self.sender == self.state_key { + MembershipChange::Left + } else { + MembershipChange::Kicked + } + } + (Join, Ban) => MembershipChange::KickedAndBanned, + (Leave, Invite) => MembershipChange::Invited, + (Ban, Leave) => MembershipChange::Unbanned, + (Knock, _) | (_, Knock) => MembershipChange::NotImplemented, + } + } +} #[cfg(test)] mod tests { @@ -197,10 +199,8 @@ mod tests { use matches::assert_matches; use serde_json::{from_value as from_json_value, json}; - use super::{ - MemberEvent, MemberEventContent, MembershipState, SignedContent, ThirdPartyInvite, - }; - use crate::EventJson; + use super::{MemberEventContent, MembershipState, SignedContent, ThirdPartyInvite}; + use crate::{EventJson, StateEvent}; #[test] fn serde_with_no_prev_content() { @@ -217,11 +217,11 @@ mod tests { }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - MemberEvent { + StateEvent:: { content: MemberEventContent { avatar_url: None, displayname: None, @@ -231,7 +231,7 @@ mod tests { }, event_id, origin_server_ts, - room_id: Some(room_id), + room_id, sender, state_key, unsigned, @@ -263,11 +263,11 @@ mod tests { }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - MemberEvent { + StateEvent:: { content: MemberEventContent { avatar_url: None, displayname: None, @@ -277,7 +277,7 @@ mod tests { }, event_id, origin_server_ts, - room_id: Some(room_id), + room_id, sender, state_key, unsigned, @@ -327,11 +327,11 @@ mod tests { }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - MemberEvent { + StateEvent:: { content: MemberEventContent { avatar_url: Some(avatar_url), displayname: Some(displayname), @@ -344,7 +344,7 @@ mod tests { }, event_id, origin_server_ts, - room_id: Some(room_id), + room_id, sender, state_key, unsigned, @@ -401,11 +401,11 @@ mod tests { }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - MemberEvent { + StateEvent:: { content: MemberEventContent { avatar_url: None, displayname: None, @@ -415,7 +415,7 @@ mod tests { }, event_id, origin_server_ts, - room_id: Some(room_id), + room_id, sender, state_key, unsigned, diff --git a/src/state.rs b/src/state.rs index 1a3bbeb4..2f28f2c7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -20,7 +20,8 @@ use serde_json::value::RawValue as RawJsonValue; use crate::{ error::{InvalidEvent, InvalidEventKind}, room::{aliases::AliasesEventContent, avatar::AvatarEventContent}, - EventContent, RoomEventContent, StateEventContent, TryFromRaw, UnsignedData, + EventContent, FromRaw, RawEventContent, RoomEventContent, StateEventContent, TryFromRaw, + UnsignedData, }; use ruma_events_macros::event_content_collection; @@ -32,7 +33,10 @@ event_content_collection! { /// State event. #[derive(Clone, Debug)] -pub struct StateEvent { +pub struct StateEvent +where + C::Raw: RawEventContent, +{ /// Data specific to the event type. pub content: C, @@ -61,6 +65,31 @@ pub struct StateEvent { pub unsigned: UnsignedData, } +impl FromRaw for AnyStateEventContent { + type Raw = raw::AnyStateEventContent; + + fn from_raw(raw: Self::Raw) -> Self { + use raw::AnyStateEventContent::*; + + match raw { + RoomAliases(c) => Self::RoomAliases(FromRaw::from_raw(c)), + RoomAvatar(c) => Self::RoomAvatar(FromRaw::from_raw(c)), + } + } +} + +impl Serialize for AnyStateEventContent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + AnyStateEventContent::RoomAliases(content) => content.serialize(serializer), + AnyStateEventContent::RoomAvatar(content) => content.serialize(serializer), + } + } +} + impl EventContent for AnyStateEventContent { fn event_type(&self) -> &str { match self { @@ -68,41 +97,38 @@ impl EventContent for AnyStateEventContent { AnyStateEventContent::RoomAvatar(content) => content.event_type(), } } - - fn from_parts(event_type: &str, content: Box) -> Result { - fn deserialize_variant( - ev_type: &str, - input: Box, - variant: fn(T) -> AnyStateEventContent, - ) -> Result { - let content = T::from_parts(ev_type, input)?; - Ok(variant(content)) - } - - match event_type { - "m.room.avatar" => deserialize_variant::( - event_type, - content, - AnyStateEventContent::RoomAvatar, - ), - "m.room.aliases" => deserialize_variant::( - 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 Serialize for StateEvent { +impl TryFromRaw for StateEvent +where + C: StateEventContent + TryFromRaw, + C::Raw: RawEventContent, +{ + type Raw = raw::StateEvent; + type Err = C::Err; + + fn try_from_raw(raw: Self::Raw) -> Result { + Ok(Self { + content: C::try_from_raw(raw.content)?, + event_id: raw.event_id, + sender: raw.sender, + origin_server_ts: raw.origin_server_ts, + room_id: raw.room_id, + state_key: raw.state_key, + prev_content: raw.prev_content.map(C::try_from_raw).transpose()?, + unsigned: raw.unsigned, + }) + } +} + +impl Serialize for StateEvent +where + C::Raw: RawEventContent, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -130,146 +156,238 @@ impl Serialize for StateEvent { } } -impl<'de, C: StateEventContent> Deserialize<'de> for StateEvent { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_map(StateEventVisitor(std::marker::PhantomData)) - } -} +mod raw { + use std::{ + fmt, + marker::PhantomData, + time::{Duration, SystemTime, UNIX_EPOCH}, + }; -#[derive(serde::Deserialize)] -#[serde(field_identifier, rename_all = "snake_case")] -enum Field { - Type, - Content, - EventId, - Sender, - OriginServerTs, - RoomId, - StateKey, - PrevContent, - Unsigned, -} + use js_int::UInt; + use ruma_events_macros::event_content_collection; + use ruma_identifiers::{EventId, RoomId, UserId}; + use serde::{ + de::{self, Deserialize, Deserializer, Error as _, MapAccess, Visitor}, + Serialize, + }; + use serde_json::value::RawValue as RawJsonValue; -/// Visits the fields of a StateEvent to handle deserialization of -/// the `content` and `prev_content` fields. -struct StateEventVisitor(PhantomData); + use crate::{ + room::{aliases::raw::AliasesEventContent, avatar::raw::AvatarEventContent}, + RawEventContent, UnsignedData, + }; -impl<'de, C: StateEventContent> Visitor<'de> for StateEventVisitor { - type Value = StateEvent; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "struct implementing StateEventContent") + event_content_collection! { + /// A state event. + name: AnyStateEventContent, + events: ["m.room.aliases", "m.room.avatar"] } - fn visit_map(self, mut map: A) -> Result - where - A: MapAccess<'de>, - { - let mut content: Option> = None; - let mut event_type: Option = None; - let mut event_id: Option = None; - let mut sender: Option = None; - let mut origin_server_ts: Option = None; - let mut room_id: Option = None; - let mut state_key: Option = None; - let mut prev_content: Option> = None; - let mut unsigned: Option = None; + impl RawEventContent for AnyStateEventContent { + fn from_parts(event_type: &str, content: Box) -> Result { + fn deserialize_variant( + ev_type: &str, + input: Box, + variant: fn(T) -> AnyStateEventContent, + ) -> Result { + let content = T::from_parts(ev_type, input)?; + Ok(variant(content)) + } - 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()?); - } - Field::Unsigned => { - if unsigned.is_some() { - return Err(de::Error::duplicate_field("unsigned")); - } - unsigned = Some(map.next_value()?); - } + match event_type { + "m.room.avatar" => deserialize_variant::( + event_type, + content, + AnyStateEventContent::RoomAvatar, + ), + "m.room.aliases" => deserialize_variant::( + event_type, + content, + AnyStateEventContent::RoomAliases, + ), + ev => Err(format!("event not supported {}", ev)), } } + } - let event_type = event_type.ok_or_else(|| de::Error::missing_field("type"))?; + /// State event. + #[derive(Clone, Debug)] + pub struct StateEvent { + /// Data specific to the event type. + pub content: C, - let raw = content.ok_or_else(|| de::Error::missing_field("content"))?; - let content = C::from_parts(&event_type, raw).map_err(A::Error::custom)?; + /// The globally unique event identifier for the user who sent the event. + pub event_id: EventId, - 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"))?; + /// Contains the fully-qualified ID of the user who sent this event. + pub sender: UserId, - 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"))?; + /// Timestamp in milliseconds on originating homeserver when this event was sent. + pub origin_server_ts: SystemTime, - 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"))?; + /// The ID of the room associated with this event. + pub room_id: RoomId, - let prev_content = if let Some(raw) = prev_content { - Some(C::from_parts(&event_type, raw).map_err(A::Error::custom)?) - } else { - None - }; + /// 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, - let unsigned = unsigned.unwrap_or_default(); + /// Optional previous content for this event. + pub prev_content: Option, - Ok(StateEvent { - content, - event_id, - sender, - origin_server_ts, - room_id, - state_key, - prev_content, - unsigned, - }) + /// Additional key-value pairs not signed by the homeserver. + pub unsigned: UnsignedData, + } + + impl<'de, C> Deserialize<'de> for StateEvent + where + C: RawEventContent, + { + fn deserialize(deserializer: D) -> Result + 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, + Unsigned, + } + + /// Visits the fields of a StateEvent to handle deserialization of + /// the `content` and `prev_content` fields. + struct StateEventVisitor(PhantomData); + + impl<'de, C> Visitor<'de> for StateEventVisitor + where + C: RawEventContent, + { + type Value = StateEvent; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "struct implementing StateEventContent") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut content: Option> = None; + let mut event_type: Option = None; + let mut event_id: Option = None; + let mut sender: Option = None; + let mut origin_server_ts: Option = None; + let mut room_id: Option = None; + let mut state_key: Option = None; + let mut prev_content: Option> = None; + let mut unsigned: Option = 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()?); + } + Field::Unsigned => { + if unsigned.is_some() { + return Err(de::Error::duplicate_field("unsigned")); + } + unsigned = 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 + }; + + let unsigned = unsigned.unwrap_or_default(); + + Ok(StateEvent { + content, + event_id, + sender, + origin_server_ts, + room_id, + state_key, + prev_content, + unsigned, + }) + } } } @@ -288,7 +406,7 @@ mod tests { use super::{AliasesEventContent, AnyStateEventContent, AvatarEventContent, StateEvent}; use crate::{ room::{ImageInfo, ThumbnailInfo}, - UnsignedData, + EventJson, UnsignedData, }; #[test] @@ -358,6 +476,22 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn deserialize_aliases_content() { + let json_data = json!({ + "aliases": [ "#somewhere:localhost" ] + }); + + assert_matches!( + from_json_value::>(json_data) + .unwrap() + .deserialize_content("m.room.aliases") + .unwrap(), + AnyStateEventContent::RoomAliases(content) + if content.aliases == vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()] + ); + } + #[test] fn deserialize_aliases_with_prev_content() { let json_data = json!({ @@ -376,7 +510,10 @@ mod tests { }); assert_matches!( - from_json_value::>(json_data).unwrap(), + from_json_value::>>(json_data) + .unwrap() + .deserialize() + .unwrap(), StateEvent { content: AnyStateEventContent::RoomAliases(content), event_id, @@ -425,7 +562,10 @@ mod tests { }); assert_matches!( - from_json_value::>(json_data).unwrap(), + from_json_value::>>(json_data) + .unwrap() + .deserialize() + .unwrap(), StateEvent { content: AnyStateEventContent::RoomAvatar(AvatarEventContent { info: Some(info),