From 05562a48a3a318f410a1aa6d3607969047cc9399 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 28 Sep 2019 01:17:38 +0200 Subject: [PATCH] WIP --- Cargo.toml | 2 +- ruma-events-macros/src/gen.rs | 105 +-- .../tests/ruma_events_macros.rs | 39 +- src/collections/all.rs | 44 +- src/collections/only.rs | 31 +- src/collections/raw/all.rs | 604 ++++++++++++++++++ src/collections/raw/only.rs | 199 ++++++ src/direct.rs | 6 +- src/dummy.rs | 6 +- src/ignored_user_list.rs | 99 +-- src/key/verification/start.rs | 119 ++-- src/lib.rs | 316 +++++---- src/macros.rs | 22 +- src/presence.rs | 9 +- src/push_rules.rs | 6 +- src/room/canonical_alias.rs | 83 +-- src/room/create.rs | 9 +- src/room/encrypted.rs | 100 +-- src/room/message.rs | 123 ++-- src/room/name.rs | 78 +-- src/room/pinned_events.rs | 8 +- src/room/power_levels.rs | 119 +--- src/room/server_acl.rs | 97 +-- src/stripped.rs | 438 +++++-------- 24 files changed, 1553 insertions(+), 1109 deletions(-) create mode 100644 src/collections/raw/all.rs create mode 100644 src/collections/raw/only.rs diff --git a/Cargo.toml b/Cargo.toml index d62f71ea..ce03493a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" [dependencies] ruma-identifiers = "0.14.0" -ruma-events-macros = { version = "0.1.0", path = "ruma-events-macros" } +ruma-events-macros = { path = "ruma-events-macros" } serde_json = "1.0.40" [dependencies.js_int] diff --git a/ruma-events-macros/src/gen.rs b/ruma-events-macros/src/gen.rs index c3fc9b96..e21f31f7 100644 --- a/ruma-events-macros/src/gen.rs +++ b/ruma-events-macros/src/gen.rs @@ -164,24 +164,11 @@ impl ToTokens for RumaEvent { let try_from_field_value = if ident == "content" { match &self.content { - Content::Struct(content_fields) => { - let mut content_field_values: Vec = - Vec::with_capacity(content_fields.len()); - - for content_field in content_fields { - let content_field_ident = content_field.ident.clone().unwrap(); - let span = content_field.span(); - - let token_stream = quote_spanned! {span=> - #content_field_ident: raw.content.#content_field_ident, - }; - - content_field_values.push(token_stream); - } - + Content::Struct(_) => { quote_spanned! {span=> - content: #content_name { - #(#content_field_values)* + content: match std::convert::TryFrom::try_from(raw.content) { + Ok(c) => c, + Err((_, void)) => match void {}, }, } } @@ -193,25 +180,12 @@ impl ToTokens for RumaEvent { } } else if ident == "prev_content" { match &self.content { - Content::Struct(content_fields) => { - let mut content_field_values: Vec = - Vec::with_capacity(content_fields.len()); - - for content_field in content_fields { - let content_field_ident = content_field.ident.clone().unwrap(); - let span = content_field.span(); - - let token_stream = quote_spanned! {span=> - #content_field_ident: prev.#content_field_ident, - }; - - content_field_values.push(token_stream); - } - + Content::Struct(_) => { quote_spanned! {span=> - prev_content: raw.prev_content.map(|prev| { - #content_name { - #(#content_field_values)* + prev_content: raw.prev_content.map(|prev_content| { + match std::convert::TryFrom::try_from(prev_content) { + Ok(c) => c, + Err((_, void)) => match void {}, } }), } @@ -360,31 +334,19 @@ impl ToTokens for RumaEvent { } quote! { - impl<'de> serde::Deserialize<'de> for crate::EventResult<#content_name> { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; + impl std::convert::TryFrom for #content_name { + type Error = (raw::#content_name, crate::Void); - let raw: raw::#content_name = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(crate::EventResult::Err(crate::InvalidEvent( - crate::InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(crate::EventResult::Ok(#content_name { + fn try_from(raw: raw::#content_name) -> Result { + Ok(Self { #(#content_field_values)* - })) + }) } } + impl crate::EventResultCompatible for #content_name { + type Raw = raw::#content_name; + } } } else { TokenStream::new() @@ -399,36 +361,25 @@ impl ToTokens for RumaEvent { #content - impl<'de> serde::Deserialize<'de> for crate::EventResult<#name> { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; + impl std::convert::TryFrom for #name { + type Error = (raw::#name, crate::Void); - let raw: raw::#name = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(crate::EventResult::Err(crate::InvalidEvent( - crate::InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(crate::EventResult::Ok(#name { + fn try_from(raw: raw::#name) -> Result { + Ok(Self { #(#try_from_field_values)* - })) + }) } } + impl crate::EventResultCompatible for #name { + type Raw = raw::#name; + } + #impl_conversions_for_content use serde::ser::SerializeStruct as _; - impl serde::Serialize for #name { + impl serde::Serialize for #name { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer @@ -464,7 +415,7 @@ impl ToTokens for RumaEvent { #impl_state_event /// "Raw" versions of the event and its content which implement `serde::Deserialize`. - mod raw { + pub(crate) mod raw { use super::*; #(#attrs)* diff --git a/ruma-events-macros/tests/ruma_events_macros.rs b/ruma-events-macros/tests/ruma_events_macros.rs index dd47dc7b..a5a12a40 100644 --- a/ruma-events-macros/tests/ruma_events_macros.rs +++ b/ruma-events-macros/tests/ruma_events_macros.rs @@ -1,4 +1,7 @@ -use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + marker::PhantomData, +}; use serde::{ de::{Error as SerdeError, Visitor}, @@ -82,19 +85,19 @@ impl<'de> Deserialize<'de> for EventType { /// The result of deserializing an event, which may or may not be valid. #[derive(Debug)] -pub enum EventResult { +pub enum EventResult { /// `T` deserialized and validated successfully. Ok(T), /// `T` deserialized but was invalid. /// /// `InvalidEvent` contains the original input. - Err(InvalidEvent), + Err(InvalidEvent), } -impl EventResult { - /// Convert `EventResult` into the equivalent `std::result::Result`. - pub fn into_result(self) -> Result { +impl EventResult { + /// Convert `EventResult` into the equivalent `std::result::Result>`. + pub fn into_result(self) -> Result> { match self { EventResult::Ok(t) => Ok(t), EventResult::Err(invalid_event) => Err(invalid_event), @@ -102,11 +105,21 @@ impl EventResult { } } +pub trait EventResultCompatible { + /// The raw form of this event that deserialization falls back to if deserializing `Self` fails. + type Raw; +} + +enum Void {} + +impl From for String { + fn from(v: Void) -> Self { + match v {} + } +} + /// A basic event. -pub trait Event -where - Self: Debug + Serialize, -{ +pub trait Event: Debug + Serialize + EventResultCompatible { /// The type of this event's `content` field. type Content: Debug + Serialize; @@ -157,11 +170,11 @@ pub trait StateEvent: RoomEvent { /// the event is otherwise invalid, a similar message will be provided, as well as a /// `serde_json::Value` containing the raw JSON data as it was deserialized. #[derive(Debug)] -pub struct InvalidEvent(InnerInvalidEvent); +pub struct InvalidEvent(InnerInvalidEvent); /// An event that is malformed or otherwise invalid. #[derive(Debug)] -enum InnerInvalidEvent { +enum InnerInvalidEvent { /// An event that deserialized but failed validation. Validation { /// The raw `serde_json::Value` representation of the invalid event. @@ -169,6 +182,8 @@ enum InnerInvalidEvent { /// An message describing why the event was invalid. message: String, + + dummy: PhantomData, }, } diff --git a/src/collections/all.rs b/src/collections/all.rs index 65125111..2276ed42 100644 --- a/src/collections/all.rs +++ b/src/collections/all.rs @@ -1,9 +1,11 @@ //! Enums for heterogeneous collections of events, inclusive for every event type that implements //! the trait of the same name. -use serde::{Serialize, Serializer}; -use serde_json::{from_value, Value}; +use std::convert::TryFrom; +use serde::{Serialize, Serializer}; + +use super::raw::all as raw; use crate::{ call::{ answer::AnswerEvent, candidates::CandidatesEvent, hangup::HangupEvent, invite::InviteEvent, @@ -46,7 +48,7 @@ use crate::{ sticker::StickerEvent, tag::TagEvent, typing::TypingEvent, - CustomEvent, CustomRoomEvent, CustomStateEvent, EventType, InnerInvalidEvent, InvalidEvent, + CustomEvent, CustomRoomEvent, CustomStateEvent, EventResultCompatible, Void, }; /// A basic event, room event, or state event. @@ -334,6 +336,42 @@ pub enum StateEvent { CustomState(CustomStateEvent), } +impl EventResultCompatible for Event { + type Raw = raw::Event; +} + +impl TryFrom for Event { + type Error = (raw::Event, Void); + + fn try_from(raw: raw::Event) -> Result { + unimplemented!() + } +} + +impl EventResultCompatible for RoomEvent { + type Raw = raw::RoomEvent; +} + +impl TryFrom for RoomEvent { + type Error = (raw::RoomEvent, Void); + + fn try_from(raw: raw::RoomEvent) -> Result { + unimplemented!() + } +} + +impl EventResultCompatible for StateEvent { + type Raw = raw::StateEvent; +} + +impl TryFrom for StateEvent { + type Error = (raw::StateEvent, Void); + + fn try_from(raw: raw::StateEvent) -> Result { + unimplemented!() + } +} + impl Serialize for Event { fn serialize(&self, serializer: S) -> Result where diff --git a/src/collections/only.rs b/src/collections/only.rs index f32666e1..8b0faae5 100644 --- a/src/collections/only.rs +++ b/src/collections/only.rs @@ -1,12 +1,11 @@ //! Enums for heterogeneous collections of events, exclusive to event types that implement "at //! most" the trait of the same name. -use std::str::FromStr; +use std::convert::TryFrom; use serde::{Serialize, Serializer}; -use serde_json::{from_value, Value}; -pub use super::all::StateEvent; +pub use super::{all::StateEvent, raw::only as raw}; use crate::{ call::{ answer::AnswerEvent, candidates::CandidatesEvent, hangup::HangupEvent, invite::InviteEvent, @@ -33,7 +32,7 @@ use crate::{ sticker::StickerEvent, tag::TagEvent, typing::TypingEvent, - CustomEvent, CustomRoomEvent, EventType, InnerInvalidEvent, InvalidEvent, + CustomEvent, CustomRoomEvent, EventResultCompatible, Void, }; /// A basic event. @@ -133,6 +132,30 @@ pub enum RoomEvent { CustomRoom(CustomRoomEvent), } +impl EventResultCompatible for Event { + type Raw = raw::Event; +} + +impl TryFrom for Event { + type Error = (raw::Event, Void); + + fn try_from(raw: raw::Event) -> Result { + unimplemented!() + } +} + +impl EventResultCompatible for RoomEvent { + type Raw = raw::RoomEvent; +} + +impl TryFrom for RoomEvent { + type Error = (raw::RoomEvent, Void); + + fn try_from(raw: raw::RoomEvent) -> Result { + unimplemented!() + } +} + impl Serialize for Event { fn serialize(&self, serializer: S) -> Result where diff --git a/src/collections/raw/all.rs b/src/collections/raw/all.rs new file mode 100644 index 00000000..e457df24 --- /dev/null +++ b/src/collections/raw/all.rs @@ -0,0 +1,604 @@ +//! Enums for heterogeneous collections of events, inclusive for every event type that implements +//! the trait of the same name. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{ + call::{ + answer::AnswerEvent, candidates::CandidatesEvent, hangup::HangupEvent, invite::InviteEvent, + }, + direct::DirectEvent, + dummy::DummyEvent, + forwarded_room_key::ForwardedRoomKeyEvent, + fully_read::FullyReadEvent, + ignored_user_list::IgnoredUserListEvent, + key::verification::{ + accept::AcceptEvent, cancel::CancelEvent, key::KeyEvent, mac::MacEvent, + request::RequestEvent, start::StartEvent, + }, + presence::PresenceEvent, + push_rules::PushRulesEvent, + receipt::ReceiptEvent, + room::{ + aliases::AliasesEvent, + avatar::AvatarEvent, + canonical_alias::CanonicalAliasEvent, + create::CreateEvent, + encrypted::EncryptedEvent, + encryption::EncryptionEvent, + guest_access::GuestAccessEvent, + history_visibility::HistoryVisibilityEvent, + join_rules::JoinRulesEvent, + member::MemberEvent, + message::{feedback::FeedbackEvent, MessageEvent}, + name::NameEvent, + pinned_events::PinnedEventsEvent, + power_levels::PowerLevelsEvent, + redaction::RedactionEvent, + server_acl::ServerAclEvent, + third_party_invite::ThirdPartyInviteEvent, + tombstone::TombstoneEvent, + topic::TopicEvent, + }, + room_key::RoomKeyEvent, + room_key_request::RoomKeyRequestEvent, + sticker::StickerEvent, + tag::TagEvent, + typing::TypingEvent, + CustomEvent, CustomRoomEvent, CustomStateEvent, +}; + +/// A basic event, room event, or state event. +#[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum Event { + /// m.call.answer + CallAnswer(AnswerEvent), + + /// m.call.candidates + CallCandidates(CandidatesEvent), + + /// m.call.hangup + CallHangup(HangupEvent), + + /// m.call.invite + CallInvite(InviteEvent), + + /// m.direct + Direct(DirectEvent), + + /// m.dummy + Dummy(DummyEvent), + + /// m.forwarded_room_key + ForwardedRoomKey(ForwardedRoomKeyEvent), + + /// m.fully_read + FullyRead(FullyReadEvent), + + /// m.ignored_user_list + IgnoredUserList(IgnoredUserListEvent), + + /// m.key.verification.accept + KeyVerificationAccept(AcceptEvent), + + /// m.key.verification.cancel + KeyVerificationCancel(CancelEvent), + + /// m.key.verification.key + KeyVerificationKey(KeyEvent), + + /// m.key.verification.mac + KeyVerificationMac(MacEvent), + + /// m.key.verification.request + KeyVerificationRequest(RequestEvent), + + /// m.key.verification.start + KeyVerificationStart(StartEvent), + + /// m.presence + Presence(PresenceEvent), + + /// m.push_rules + PushRules(PushRulesEvent), + + /// m.receipt + Receipt(ReceiptEvent), + + /// m.room.aliases + RoomAliases(AliasesEvent), + + /// m.room.avatar + RoomAvatar(AvatarEvent), + + /// m.room.canonical_alias + RoomCanonicalAlias(CanonicalAliasEvent), + + /// m.room.create + RoomCreate(CreateEvent), + + /// m.room.encrypted + RoomEncrypted(EncryptedEvent), + + /// m.room.encryption + RoomEncryption(EncryptionEvent), + + /// m.room.guest_access + RoomGuestAccess(GuestAccessEvent), + + /// m.room.history_visibility + RoomHistoryVisibility(HistoryVisibilityEvent), + + /// m.room.join_rules + RoomJoinRules(JoinRulesEvent), + + /// m.room.member + RoomMember(MemberEvent), + + /// m.room.message + RoomMessage(MessageEvent), + + /// m.room.message.feedback + RoomMessageFeedback(FeedbackEvent), + + /// m.room.name + RoomName(NameEvent), + + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), + + /// m.room.power_levels + RoomPowerLevels(PowerLevelsEvent), + + /// m.room.redaction + RoomRedaction(RedactionEvent), + + /// m.room.server_acl, + RoomServerAcl(ServerAclEvent), + + /// m.room.third_party_invite + RoomThirdPartyInvite(ThirdPartyInviteEvent), + + /// m.room.tombstone + RoomTombstone(TombstoneEvent), + + /// m.room.topic + RoomTopic(TopicEvent), + + /// m.room_key + RoomKey(RoomKeyEvent), + + /// m.room_key_request + RoomKeyRequest(RoomKeyRequestEvent), + + /// m.sticker + Sticker(StickerEvent), + + /// m.tag + Tag(TagEvent), + + /// m.typing + Typing(TypingEvent), + + /// Any basic event that is not part of the specification. + Custom(CustomEvent), + + /// Any room event that is not part of the specification. + CustomRoom(CustomRoomEvent), + + /// Any state event that is not part of the specification. + CustomState(CustomStateEvent), +} + +/// A room event or state event. +#[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum RoomEvent { + /// m.call.answer + CallAnswer(AnswerEvent), + + /// m.call.candidates + CallCandidates(CandidatesEvent), + + /// m.call.hangup + CallHangup(HangupEvent), + + /// m.call.invite + CallInvite(InviteEvent), + + /// m.room.aliases + RoomAliases(AliasesEvent), + + /// m.room.avatar + RoomAvatar(AvatarEvent), + + /// m.room.canonical_alias + RoomCanonicalAlias(CanonicalAliasEvent), + + /// m.room.create + RoomCreate(CreateEvent), + + /// m.room.encrypted + RoomEncrypted(EncryptedEvent), + + /// m.room.encryption + RoomEncryption(EncryptionEvent), + + /// m.room.guest_access + RoomGuestAccess(GuestAccessEvent), + + /// m.room.history_visibility + RoomHistoryVisibility(HistoryVisibilityEvent), + + /// m.room.join_rules + RoomJoinRules(JoinRulesEvent), + + /// m.room.member + RoomMember(MemberEvent), + + /// m.room.message + RoomMessage(MessageEvent), + + /// m.room.message.feedback + RoomMessageFeedback(FeedbackEvent), + + /// m.room.name + RoomName(NameEvent), + + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), + + /// m.room.power_levels + RoomPowerLevels(PowerLevelsEvent), + + /// m.room.redaction + RoomRedaction(RedactionEvent), + + /// m.room.server_acl, + RoomServerAcl(ServerAclEvent), + + /// m.room.third_party_invite + RoomThirdPartyInvite(ThirdPartyInviteEvent), + + /// m.room.tombstone + RoomTombstone(TombstoneEvent), + + /// m.room.topic + RoomTopic(TopicEvent), + + /// m.sticker + Sticker(StickerEvent), + + /// Any room event that is not part of the specification. + CustomRoom(CustomRoomEvent), + + /// Any state event that is not part of the specification. + CustomState(CustomStateEvent), +} + +/// A state event. +#[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum StateEvent { + /// m.room.aliases + RoomAliases(AliasesEvent), + + /// m.room.avatar + RoomAvatar(AvatarEvent), + + /// m.room.canonical_alias + RoomCanonicalAlias(CanonicalAliasEvent), + + /// m.room.create + RoomCreate(CreateEvent), + + /// m.room.encryption + RoomEncryption(EncryptionEvent), + + /// m.room.guest_access + RoomGuestAccess(GuestAccessEvent), + + /// m.room.history_visibility + RoomHistoryVisibility(HistoryVisibilityEvent), + + /// m.room.join_rules + RoomJoinRules(JoinRulesEvent), + + /// m.room.member + RoomMember(MemberEvent), + + /// m.room.name + RoomName(NameEvent), + + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), + + /// m.room.power_levels + RoomPowerLevels(PowerLevelsEvent), + + /// m.room.server_acl, + RoomServerAcl(ServerAclEvent), + + /// m.room.third_party_invite + RoomThirdPartyInvite(ThirdPartyInviteEvent), + + /// m.room.tombstone + RoomTombstone(TombstoneEvent), + + /// m.room.topic + RoomTopic(TopicEvent), + + /// Any state event that is not part of the specification. + CustomState(CustomStateEvent), +} + +impl<'de> Deserialize<'de> for Event { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} + +impl<'de> Deserialize<'de> for RoomEvent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} + +impl<'de> Deserialize<'de> for StateEvent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} + +impl Serialize for Event { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Event::CallAnswer(ref event) => event.serialize(serializer), + Event::CallCandidates(ref event) => event.serialize(serializer), + Event::CallHangup(ref event) => event.serialize(serializer), + Event::CallInvite(ref event) => event.serialize(serializer), + Event::Direct(ref event) => event.serialize(serializer), + Event::Dummy(ref event) => event.serialize(serializer), + Event::ForwardedRoomKey(ref event) => event.serialize(serializer), + Event::FullyRead(ref event) => event.serialize(serializer), + Event::KeyVerificationAccept(ref event) => event.serialize(serializer), + Event::KeyVerificationCancel(ref event) => event.serialize(serializer), + Event::KeyVerificationKey(ref event) => event.serialize(serializer), + Event::KeyVerificationMac(ref event) => event.serialize(serializer), + Event::KeyVerificationRequest(ref event) => event.serialize(serializer), + Event::KeyVerificationStart(ref event) => event.serialize(serializer), + Event::IgnoredUserList(ref event) => event.serialize(serializer), + Event::Presence(ref event) => event.serialize(serializer), + Event::PushRules(ref event) => event.serialize(serializer), + Event::Receipt(ref event) => event.serialize(serializer), + Event::RoomAliases(ref event) => event.serialize(serializer), + Event::RoomAvatar(ref event) => event.serialize(serializer), + Event::RoomCanonicalAlias(ref event) => event.serialize(serializer), + Event::RoomCreate(ref event) => event.serialize(serializer), + Event::RoomEncrypted(ref event) => event.serialize(serializer), + Event::RoomEncryption(ref event) => event.serialize(serializer), + Event::RoomGuestAccess(ref event) => event.serialize(serializer), + Event::RoomHistoryVisibility(ref event) => event.serialize(serializer), + Event::RoomJoinRules(ref event) => event.serialize(serializer), + Event::RoomMember(ref event) => event.serialize(serializer), + Event::RoomMessage(ref event) => event.serialize(serializer), + Event::RoomMessageFeedback(ref event) => event.serialize(serializer), + Event::RoomName(ref event) => event.serialize(serializer), + Event::RoomPinnedEvents(ref event) => event.serialize(serializer), + Event::RoomPowerLevels(ref event) => event.serialize(serializer), + Event::RoomRedaction(ref event) => event.serialize(serializer), + Event::RoomServerAcl(ref event) => event.serialize(serializer), + Event::RoomThirdPartyInvite(ref event) => event.serialize(serializer), + Event::RoomTombstone(ref event) => event.serialize(serializer), + Event::RoomTopic(ref event) => event.serialize(serializer), + Event::RoomKey(ref event) => event.serialize(serializer), + Event::RoomKeyRequest(ref event) => event.serialize(serializer), + Event::Sticker(ref event) => event.serialize(serializer), + Event::Tag(ref event) => event.serialize(serializer), + Event::Typing(ref event) => event.serialize(serializer), + Event::Custom(ref event) => event.serialize(serializer), + Event::CustomRoom(ref event) => event.serialize(serializer), + Event::CustomState(ref event) => event.serialize(serializer), + } + } +} + +impl Serialize for RoomEvent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + RoomEvent::CallAnswer(ref event) => event.serialize(serializer), + RoomEvent::CallCandidates(ref event) => event.serialize(serializer), + RoomEvent::CallHangup(ref event) => event.serialize(serializer), + RoomEvent::CallInvite(ref event) => event.serialize(serializer), + RoomEvent::RoomAliases(ref event) => event.serialize(serializer), + RoomEvent::RoomAvatar(ref event) => event.serialize(serializer), + RoomEvent::RoomCanonicalAlias(ref event) => event.serialize(serializer), + RoomEvent::RoomCreate(ref event) => event.serialize(serializer), + RoomEvent::RoomEncrypted(ref event) => event.serialize(serializer), + RoomEvent::RoomEncryption(ref event) => event.serialize(serializer), + RoomEvent::RoomGuestAccess(ref event) => event.serialize(serializer), + RoomEvent::RoomHistoryVisibility(ref event) => event.serialize(serializer), + RoomEvent::RoomJoinRules(ref event) => event.serialize(serializer), + RoomEvent::RoomMember(ref event) => event.serialize(serializer), + RoomEvent::RoomMessage(ref event) => event.serialize(serializer), + RoomEvent::RoomMessageFeedback(ref event) => event.serialize(serializer), + RoomEvent::RoomName(ref event) => event.serialize(serializer), + RoomEvent::RoomPinnedEvents(ref event) => event.serialize(serializer), + RoomEvent::RoomPowerLevels(ref event) => event.serialize(serializer), + RoomEvent::RoomRedaction(ref event) => event.serialize(serializer), + RoomEvent::RoomServerAcl(ref event) => event.serialize(serializer), + RoomEvent::RoomThirdPartyInvite(ref event) => event.serialize(serializer), + RoomEvent::RoomTombstone(ref event) => event.serialize(serializer), + RoomEvent::RoomTopic(ref event) => event.serialize(serializer), + RoomEvent::Sticker(ref event) => event.serialize(serializer), + RoomEvent::CustomRoom(ref event) => event.serialize(serializer), + RoomEvent::CustomState(ref event) => event.serialize(serializer), + } + } +} + +impl Serialize for StateEvent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + StateEvent::RoomAliases(ref event) => event.serialize(serializer), + StateEvent::RoomAvatar(ref event) => event.serialize(serializer), + StateEvent::RoomCanonicalAlias(ref event) => event.serialize(serializer), + StateEvent::RoomCreate(ref event) => event.serialize(serializer), + StateEvent::RoomEncryption(ref event) => event.serialize(serializer), + StateEvent::RoomGuestAccess(ref event) => event.serialize(serializer), + StateEvent::RoomHistoryVisibility(ref event) => event.serialize(serializer), + StateEvent::RoomJoinRules(ref event) => event.serialize(serializer), + StateEvent::RoomMember(ref event) => event.serialize(serializer), + StateEvent::RoomName(ref event) => event.serialize(serializer), + StateEvent::RoomPinnedEvents(ref event) => event.serialize(serializer), + StateEvent::RoomPowerLevels(ref event) => event.serialize(serializer), + StateEvent::RoomServerAcl(ref event) => event.serialize(serializer), + StateEvent::RoomThirdPartyInvite(ref event) => event.serialize(serializer), + StateEvent::RoomTombstone(ref event) => event.serialize(serializer), + StateEvent::RoomTopic(ref event) => event.serialize(serializer), + StateEvent::CustomState(ref event) => event.serialize(serializer), + } + } +} + +macro_rules! impl_from_t_for_event { + ($ty:ty, $variant:ident) => { + impl From<$ty> for Event { + fn from(event: $ty) -> Self { + Event::$variant(event) + } + } + }; +} + +impl_from_t_for_event!(AnswerEvent, CallAnswer); +impl_from_t_for_event!(CandidatesEvent, CallCandidates); +impl_from_t_for_event!(HangupEvent, CallHangup); +impl_from_t_for_event!(InviteEvent, CallInvite); +impl_from_t_for_event!(DirectEvent, Direct); +impl_from_t_for_event!(DummyEvent, Dummy); +impl_from_t_for_event!(ForwardedRoomKeyEvent, ForwardedRoomKey); +impl_from_t_for_event!(FullyReadEvent, FullyRead); +impl_from_t_for_event!(AcceptEvent, KeyVerificationAccept); +impl_from_t_for_event!(CancelEvent, KeyVerificationCancel); +impl_from_t_for_event!(KeyEvent, KeyVerificationKey); +impl_from_t_for_event!(MacEvent, KeyVerificationMac); +impl_from_t_for_event!(RequestEvent, KeyVerificationRequest); +impl_from_t_for_event!(StartEvent, KeyVerificationStart); +impl_from_t_for_event!(IgnoredUserListEvent, IgnoredUserList); +impl_from_t_for_event!(PresenceEvent, Presence); +impl_from_t_for_event!(PushRulesEvent, PushRules); +impl_from_t_for_event!(ReceiptEvent, Receipt); +impl_from_t_for_event!(AliasesEvent, RoomAliases); +impl_from_t_for_event!(AvatarEvent, RoomAvatar); +impl_from_t_for_event!(CanonicalAliasEvent, RoomCanonicalAlias); +impl_from_t_for_event!(CreateEvent, RoomCreate); +impl_from_t_for_event!(EncryptedEvent, RoomEncrypted); +impl_from_t_for_event!(EncryptionEvent, RoomEncryption); +impl_from_t_for_event!(GuestAccessEvent, RoomGuestAccess); +impl_from_t_for_event!(HistoryVisibilityEvent, RoomHistoryVisibility); +impl_from_t_for_event!(JoinRulesEvent, RoomJoinRules); +impl_from_t_for_event!(MemberEvent, RoomMember); +impl_from_t_for_event!(MessageEvent, RoomMessage); +impl_from_t_for_event!(FeedbackEvent, RoomMessageFeedback); +impl_from_t_for_event!(NameEvent, RoomName); +impl_from_t_for_event!(PinnedEventsEvent, RoomPinnedEvents); +impl_from_t_for_event!(PowerLevelsEvent, RoomPowerLevels); +impl_from_t_for_event!(RedactionEvent, RoomRedaction); +impl_from_t_for_event!(ServerAclEvent, RoomServerAcl); +impl_from_t_for_event!(ThirdPartyInviteEvent, RoomThirdPartyInvite); +impl_from_t_for_event!(TombstoneEvent, RoomTombstone); +impl_from_t_for_event!(TopicEvent, RoomTopic); +impl_from_t_for_event!(RoomKeyEvent, RoomKey); +impl_from_t_for_event!(RoomKeyRequestEvent, RoomKeyRequest); +impl_from_t_for_event!(StickerEvent, Sticker); +impl_from_t_for_event!(TagEvent, Tag); +impl_from_t_for_event!(TypingEvent, Typing); +impl_from_t_for_event!(CustomEvent, Custom); +impl_from_t_for_event!(CustomRoomEvent, CustomRoom); +impl_from_t_for_event!(CustomStateEvent, CustomState); + +macro_rules! impl_from_t_for_room_event { + ($ty:ty, $variant:ident) => { + impl From<$ty> for RoomEvent { + fn from(event: $ty) -> Self { + RoomEvent::$variant(event) + } + } + }; +} + +impl_from_t_for_room_event!(AnswerEvent, CallAnswer); +impl_from_t_for_room_event!(CandidatesEvent, CallCandidates); +impl_from_t_for_room_event!(HangupEvent, CallHangup); +impl_from_t_for_room_event!(InviteEvent, CallInvite); +impl_from_t_for_room_event!(AliasesEvent, RoomAliases); +impl_from_t_for_room_event!(AvatarEvent, RoomAvatar); +impl_from_t_for_room_event!(CanonicalAliasEvent, RoomCanonicalAlias); +impl_from_t_for_room_event!(CreateEvent, RoomCreate); +impl_from_t_for_room_event!(EncryptedEvent, RoomEncrypted); +impl_from_t_for_room_event!(EncryptionEvent, RoomEncryption); +impl_from_t_for_room_event!(GuestAccessEvent, RoomGuestAccess); +impl_from_t_for_room_event!(HistoryVisibilityEvent, RoomHistoryVisibility); +impl_from_t_for_room_event!(JoinRulesEvent, RoomJoinRules); +impl_from_t_for_room_event!(MemberEvent, RoomMember); +impl_from_t_for_room_event!(MessageEvent, RoomMessage); +impl_from_t_for_room_event!(FeedbackEvent, RoomMessageFeedback); +impl_from_t_for_room_event!(NameEvent, RoomName); +impl_from_t_for_room_event!(PinnedEventsEvent, RoomPinnedEvents); +impl_from_t_for_room_event!(PowerLevelsEvent, RoomPowerLevels); +impl_from_t_for_room_event!(RedactionEvent, RoomRedaction); +impl_from_t_for_room_event!(ServerAclEvent, RoomServerAcl); +impl_from_t_for_room_event!(StickerEvent, Sticker); +impl_from_t_for_room_event!(ThirdPartyInviteEvent, RoomThirdPartyInvite); +impl_from_t_for_room_event!(TombstoneEvent, RoomTombstone); +impl_from_t_for_room_event!(TopicEvent, RoomTopic); +impl_from_t_for_room_event!(CustomRoomEvent, CustomRoom); +impl_from_t_for_room_event!(CustomStateEvent, CustomState); + +macro_rules! impl_from_t_for_state_event { + ($ty:ty, $variant:ident) => { + impl From<$ty> for StateEvent { + fn from(event: $ty) -> Self { + StateEvent::$variant(event) + } + } + }; +} + +impl_from_t_for_state_event!(AliasesEvent, RoomAliases); +impl_from_t_for_state_event!(AvatarEvent, RoomAvatar); +impl_from_t_for_state_event!(CanonicalAliasEvent, RoomCanonicalAlias); +impl_from_t_for_state_event!(CreateEvent, RoomCreate); +impl_from_t_for_state_event!(EncryptionEvent, RoomEncryption); +impl_from_t_for_state_event!(GuestAccessEvent, RoomGuestAccess); +impl_from_t_for_state_event!(HistoryVisibilityEvent, RoomHistoryVisibility); +impl_from_t_for_state_event!(JoinRulesEvent, RoomJoinRules); +impl_from_t_for_state_event!(MemberEvent, RoomMember); +impl_from_t_for_state_event!(NameEvent, RoomName); +impl_from_t_for_state_event!(PinnedEventsEvent, RoomPinnedEvents); +impl_from_t_for_state_event!(PowerLevelsEvent, RoomPowerLevels); +impl_from_t_for_state_event!(ServerAclEvent, RoomServerAcl); +impl_from_t_for_state_event!(ThirdPartyInviteEvent, RoomThirdPartyInvite); +impl_from_t_for_state_event!(TombstoneEvent, RoomTombstone); +impl_from_t_for_state_event!(TopicEvent, RoomTopic); +impl_from_t_for_state_event!(CustomStateEvent, CustomState); diff --git a/src/collections/raw/only.rs b/src/collections/raw/only.rs new file mode 100644 index 00000000..5956b8a3 --- /dev/null +++ b/src/collections/raw/only.rs @@ -0,0 +1,199 @@ +//! Enums for heterogeneous collections of events, exclusive to event types that implement "at +//! most" the trait of the same name. + +use serde::{Deserialize, Deserializer}; + +pub use super::all::StateEvent; +use crate::{ + call::{ + answer::raw::AnswerEvent, candidates::raw::CandidatesEvent, hangup::raw::HangupEvent, + invite::raw::InviteEvent, + }, + direct::raw::DirectEvent, + dummy::raw::DummyEvent, + forwarded_room_key::raw::ForwardedRoomKeyEvent, + fully_read::raw::FullyReadEvent, + ignored_user_list::raw::IgnoredUserListEvent, + key::verification::{ + accept::raw::AcceptEvent, cancel::raw::CancelEvent, key::raw::KeyEvent, mac::raw::MacEvent, + request::raw::RequestEvent, start::raw::StartEvent, + }, + presence::raw::PresenceEvent, + push_rules::raw::PushRulesEvent, + receipt::raw::ReceiptEvent, + room::{ + encrypted::raw::EncryptedEvent, + message::{feedback::raw::FeedbackEvent, raw::MessageEvent}, + redaction::raw::RedactionEvent, + }, + room_key::raw::RoomKeyEvent, + room_key_request::raw::RoomKeyRequestEvent, + sticker::raw::StickerEvent, + tag::raw::TagEvent, + typing::raw::TypingEvent, + CustomEvent, CustomRoomEvent, +}; + +/// A basic event. +#[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum Event { + /// m.direct + Direct(DirectEvent), + + /// m.dummy + Dummy(DummyEvent), + + /// m.forwarded_room_key + ForwardedRoomKey(ForwardedRoomKeyEvent), + + /// m.fully_read + FullyRead(FullyReadEvent), + + /// m.key.verification.accept + KeyVerificationAccept(AcceptEvent), + + /// m.key.verification.cancel + KeyVerificationCancel(CancelEvent), + + /// m.key.verification.key + KeyVerificationKey(KeyEvent), + + /// m.key.verification.mac + KeyVerificationMac(MacEvent), + + /// m.key.verification.request + KeyVerificationRequest(RequestEvent), + + /// m.key.verification.start + KeyVerificationStart(StartEvent), + + /// m.ignored_user_list + IgnoredUserList(IgnoredUserListEvent), + + /// m.presence + Presence(PresenceEvent), + + /// m.push_rules + PushRules(PushRulesEvent), + + /// m.room_key + RoomKey(RoomKeyEvent), + + /// m.room_key_request + RoomKeyRequest(RoomKeyRequestEvent), + + /// m.receipt + Receipt(ReceiptEvent), + + /// m.tag + Tag(TagEvent), + + /// m.typing + Typing(TypingEvent), + + /// Any basic event that is not part of the specification. + Custom(CustomEvent), +} + +/// A room event. +#[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] +pub enum RoomEvent { + /// m.call.answer + CallAnswer(AnswerEvent), + + /// m.call.candidates + CallCandidates(CandidatesEvent), + + /// m.call.hangup + CallHangup(HangupEvent), + + /// m.call.invite + CallInvite(InviteEvent), + + /// m.room.encrypted + RoomEncrypted(EncryptedEvent), + + /// m.room.message + RoomMessage(MessageEvent), + + /// m.room.message.feedback + RoomMessageFeedback(FeedbackEvent), + + /// m.room.redaction + RoomRedaction(RedactionEvent), + + /// m.sticker + Sticker(StickerEvent), + + /// Any room event that is not part of the specification. + CustomRoom(CustomRoomEvent), +} + +macro_rules! impl_from_t_for_event { + ($ty:ty, $variant:ident) => { + impl From<$ty> for Event { + fn from(event: $ty) -> Self { + Event::$variant(event) + } + } + }; +} + +impl<'de> Deserialize<'de> for Event { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} + +impl<'de> Deserialize<'de> for RoomEvent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } +} + +impl_from_t_for_event!(DirectEvent, Direct); +impl_from_t_for_event!(DummyEvent, Dummy); +impl_from_t_for_event!(ForwardedRoomKeyEvent, ForwardedRoomKey); +impl_from_t_for_event!(FullyReadEvent, FullyRead); +impl_from_t_for_event!(AcceptEvent, KeyVerificationAccept); +impl_from_t_for_event!(CancelEvent, KeyVerificationCancel); +impl_from_t_for_event!(KeyEvent, KeyVerificationKey); +impl_from_t_for_event!(MacEvent, KeyVerificationMac); +impl_from_t_for_event!(RequestEvent, KeyVerificationRequest); +impl_from_t_for_event!(StartEvent, KeyVerificationStart); +impl_from_t_for_event!(IgnoredUserListEvent, IgnoredUserList); +impl_from_t_for_event!(PresenceEvent, Presence); +impl_from_t_for_event!(PushRulesEvent, PushRules); +impl_from_t_for_event!(ReceiptEvent, Receipt); +impl_from_t_for_event!(TagEvent, Tag); +impl_from_t_for_event!(TypingEvent, Typing); +impl_from_t_for_event!(CustomEvent, Custom); + +macro_rules! impl_from_t_for_room_event { + ($ty:ty, $variant:ident) => { + impl From<$ty> for RoomEvent { + fn from(event: $ty) -> Self { + RoomEvent::$variant(event) + } + } + }; +} + +impl_from_t_for_room_event!(AnswerEvent, CallAnswer); +impl_from_t_for_room_event!(CandidatesEvent, CallCandidates); +impl_from_t_for_room_event!(HangupEvent, CallHangup); +impl_from_t_for_room_event!(InviteEvent, CallInvite); +impl_from_t_for_room_event!(EncryptedEvent, RoomEncrypted); +impl_from_t_for_room_event!(MessageEvent, RoomMessage); +impl_from_t_for_room_event!(FeedbackEvent, RoomMessageFeedback); +impl_from_t_for_room_event!(RedactionEvent, RoomRedaction); +impl_from_t_for_room_event!(StickerEvent, Sticker); +impl_from_t_for_room_event!(CustomRoomEvent, CustomRoom); diff --git a/src/direct.rs b/src/direct.rs index d1b9262f..bda9446a 100644 --- a/src/direct.rs +++ b/src/direct.rs @@ -28,6 +28,7 @@ mod tests { use serde_json::to_string; use super::{DirectEvent, DirectEventContent}; + use crate::EventResult; #[test] fn serialization() { @@ -67,7 +68,10 @@ mod tests { rooms[1].to_string() ); - let event: DirectEvent = json_data.parse().unwrap(); + let event: DirectEvent = serde_json::from_str::>(&json_data) + .unwrap() + .into_result() + .unwrap(); let direct_rooms = event.content.get(&alice).unwrap(); assert!(direct_rooms.contains(&rooms[0])); diff --git a/src/dummy.rs b/src/dummy.rs index 2e53d0d2..b0e78f6f 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -27,6 +27,7 @@ ruma_event! { #[cfg(test)] mod tests { use super::{DummyEvent, Empty}; + use crate::EventResult; #[test] fn serialization() { @@ -42,6 +43,9 @@ mod tests { fn deserialization() { let json = r#"{"content":{},"type":"m.dummy"}"#; - assert!(json.parse::().is_ok()); + assert!(serde_json::from_str::>(json) + .unwrap() + .into_result() + .is_ok()); } } diff --git a/src/ignored_user_list.rs b/src/ignored_user_list.rs index 822fbbab..e046088f 100644 --- a/src/ignored_user_list.rs +++ b/src/ignored_user_list.rs @@ -1,11 +1,11 @@ //! Types for the *m.ignored_user_list* event. -use std::collections::HashMap; +use std::convert::TryFrom; use ruma_identifiers::UserId; -use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; -use crate::{Empty, Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent}; +use crate::{vec_as_map_of_empty, Event as _, EventType, Void}; /// A list of users to ignore. #[derive(Clone, Debug, PartialEq)] @@ -15,36 +15,29 @@ pub struct IgnoredUserListEvent { } /// The payload for `IgnoredUserListEvent`. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub struct IgnoredUserListEventContent { /// A list of users to ignore. pub ignored_users: Vec, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for IgnoredUserListEvent { + type Error = (raw::IgnoredUserListEvent, Void); - let raw: raw::IgnoredUserListEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; + fn try_from(raw: raw::IgnoredUserListEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), + }) + } +} - Ok(EventResult::Ok(IgnoredUserListEvent { - content: IgnoredUserListEventContent { - ignored_users: raw.content.ignored_users.keys().cloned().collect(), - }, - })) +impl TryFrom for IgnoredUserListEventContent { + type Error = (raw::IgnoredUserListEventContent, Void); + + fn try_from(raw: raw::IgnoredUserListEventContent) -> Result { + Ok(Self { + ignored_users: raw.ignored_users, + }) } } @@ -65,54 +58,12 @@ impl Serialize for IgnoredUserListEvent { impl_event!( IgnoredUserListEvent, IgnoredUserListEventContent, - EventType::IgnoredUserList + EventType::IgnoredUserList, + raw ); -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::IgnoredUserListEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(IgnoredUserListEventContent { - ignored_users: raw.ignored_users.keys().cloned().collect(), - })) - } -} - -impl Serialize for IgnoredUserListEventContent { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = HashMap::new(); - - for user_id in &self.ignored_users { - map.insert(user_id.clone(), Empty); - } - - let raw = raw::IgnoredUserListEventContent { ignored_users: map }; - - raw.serialize(serializer) - } -} - -mod raw { +pub(crate) mod raw { use super::*; - use crate::Empty; /// A list of users to ignore. #[derive(Clone, Debug, Deserialize)] @@ -125,7 +76,8 @@ mod raw { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct IgnoredUserListEventContent { /// A list of users to ignore. - pub ignored_users: HashMap, + #[serde(with = "vec_as_map_of_empty")] + pub ignored_users: Vec, } } @@ -135,7 +87,8 @@ mod tests { use ruma_identifiers::UserId; - use super::{EventResult, IgnoredUserListEvent, IgnoredUserListEventContent}; + use super::{IgnoredUserListEvent, IgnoredUserListEventContent}; + use crate::EventResult; #[test] fn serialization() { diff --git a/src/key/verification/start.rs b/src/key/verification/start.rs index b90cf36f..db5f466a 100644 --- a/src/key/verification/start.rs +++ b/src/key/verification/start.rs @@ -1,5 +1,7 @@ //! Types for the *m.key.verification.start* event. +use std::convert::{TryFrom, TryInto as _}; + use ruma_identifiers::DeviceId; use serde::{de::Error, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{from_value, Value}; @@ -8,7 +10,7 @@ use super::{ HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString, VerificationMethod, }; -use crate::{Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, InvalidInput}; +use crate::{Event, EventType, InvalidInput}; /// Begins an SAS key verification process. /// @@ -31,38 +33,14 @@ pub enum StartEventContent { __Nonexhaustive, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for StartEvent { + type Error = (raw::StartEvent, &'static str); - let raw: raw::StartEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - let content = match StartEventContent::from_raw(raw.content) { - Ok(content) => content, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(StartEvent { content })) + fn try_from(raw: raw::StartEvent) -> Result { + match raw.content.try_into() { + Ok(content) => Ok(Self { content }), + Err((content, msg)) => Err((raw::StartEvent { content }, msg)), + } } } @@ -83,78 +61,62 @@ impl Serialize for StartEvent { impl_event!( StartEvent, StartEventContent, - EventType::KeyVerificationStart + EventType::KeyVerificationStart, + raw ); -impl StartEventContent { - fn from_raw(raw: raw::StartEventContent) -> Result { +impl TryFrom for StartEventContent { + type Error = (raw::StartEventContent, &'static str); + + fn try_from(raw: raw::StartEventContent) -> Result { match raw { raw::StartEventContent::MSasV1(content) => { if !content .key_agreement_protocols .contains(&KeyAgreementProtocol::Curve25519) { - return Err("`key_agreement_protocols` must contain at least `KeyAgreementProtocol::Curve25519`"); + return Err( + (raw::StartEventContent::MSasV1(content), + "`key_agreement_protocols` must contain at least `KeyAgreementProtocol::Curve25519`", + )); } if !content.hashes.contains(&HashAlgorithm::Sha256) { - return Err("`hashes` must contain at least `HashAlgorithm::Sha256`"); + return Err(( + raw::StartEventContent::MSasV1(content), + "`hashes` must contain at least `HashAlgorithm::Sha256`", + )); } if !content .message_authentication_codes .contains(&MessageAuthenticationCode::HkdfHmacSha256) { - return Err("`message_authentication_codes` must contain at least `MessageAuthenticationCode::HkdfHmacSha256`"); + return Err( + (raw::StartEventContent::MSasV1(content), + "`message_authentication_codes` must contain at least `MessageAuthenticationCode::HkdfHmacSha256`", + )); } if !content .short_authentication_string .contains(&ShortAuthenticationString::Decimal) { - return Err("`short_authentication_string` must contain at least `ShortAuthenticationString::Decimal`"); + return Err( + (raw::StartEventContent::MSasV1(content), + "`short_authentication_string` must contain at least `ShortAuthenticationString::Decimal`", + )); } Ok(StartEventContent::MSasV1(content)) } raw::StartEventContent::__Nonexhaustive => { - panic!("__Nonexhaustive enum variant is not intended for use."); + panic!("__Nonexhaustive enum variant is not intended for use.".to_owned()); } } } } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::StartEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - match StartEventContent::from_raw(raw) { - Ok(content) => Ok(EventResult::Ok(content)), - Err(error) => Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))), - } - } -} - impl Serialize for StartEventContent { fn serialize(&self, serializer: S) -> Result where @@ -167,7 +129,7 @@ impl Serialize for StartEventContent { } } -mod raw { +pub(crate) mod raw { use super::*; /// Begins an SAS key verification process. @@ -374,9 +336,10 @@ mod tests { use serde_json::to_string; use super::{ - EventResult, HashAlgorithm, KeyAgreementProtocol, MSasV1Content, MSasV1ContentOptions, + HashAlgorithm, KeyAgreementProtocol, MSasV1Content, MSasV1ContentOptions, MessageAuthenticationCode, ShortAuthenticationString, StartEvent, StartEventContent, }; + use crate::EventResult; #[test] fn invalid_m_sas_v1_content_missing_required_key_agreement_protocols() { @@ -536,7 +499,7 @@ mod tests { .unwrap_err(); assert!(error.message().contains("key_agreement_protocols")); - assert!(error.json().is_some()); + assert!(error.raw_data().is_some()); } #[test] @@ -550,7 +513,7 @@ mod tests { .unwrap_err(); assert!(error.message().contains("hashes")); - assert!(error.json().is_some()); + assert!(error.raw_data().is_some()); } #[test] @@ -564,7 +527,7 @@ mod tests { .unwrap_err(); assert!(error.message().contains("message_authentication_codes")); - assert!(error.json().is_some()); + assert!(error.raw_data().is_some()); } #[test] @@ -578,7 +541,7 @@ mod tests { .unwrap_err(); assert!(error.message().contains("short_authentication_string")); - assert!(error.json().is_some()); + assert!(error.raw_data().is_some()); } #[test] @@ -593,6 +556,6 @@ mod tests { .unwrap_err(); assert!(error.message().contains("key_agreement_protocols")); - assert!(error.json().is_some()); + assert!(error.raw_data().is_some()); } } diff --git a/src/lib.rs b/src/lib.rs index 230ba1cb..95529064 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,37 +112,41 @@ #![deny(missing_debug_implementations)] #![deny(missing_docs)] -#![deny(warnings)] +//#![deny(warnings)] use std::{ - collections::HashMap, + convert::TryInto, error::Error, fmt::{Debug, Display, Error as FmtError, Formatter, Result as FmtResult}, - hash::Hash, }; use js_int::UInt; use ruma_identifiers::{EventId, RoomId, UserId}; use serde::{ - de::{Error as SerdeError, IntoDeserializer, MapAccess, Visitor}, + de::{DeserializeOwned, Error as SerdeError, IntoDeserializer, MapAccess, Visitor}, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer, }; use serde_json::Value; -// pub use custom::CustomEvent; -// pub use custom_room::CustomRoomEvent; -// pub use custom_state::CustomStateEvent; +pub use custom::CustomEvent; +pub use custom_room::CustomRoomEvent; +pub use custom_state::CustomStateEvent; #[macro_use] mod macros; pub mod call; -// /// Enums for heterogeneous collections of events. -// pub mod collections { -// pub mod all; -// pub mod only; -// } +/// Enums for heterogeneous collections of events. +pub mod collections { + pub mod all; + pub mod only; + + mod raw { + pub mod all; + pub mod only; + } +} pub mod direct; pub mod dummy; pub mod forwarded_room_key; @@ -168,60 +172,65 @@ pub mod typing; /// provide a message with details about the deserialization error. If deserialization succeeds but /// the event is otherwise invalid, a similar message will be provided, as well as a /// `serde_json::Value` containing the raw JSON data as it was deserialized. -#[derive(Debug)] -pub struct InvalidEvent(InnerInvalidEvent); +#[derive(Clone, Debug)] +pub struct InvalidEvent(InnerInvalidEvent); -impl InvalidEvent { +impl InvalidEvent { /// A message describing why the event is invalid. pub fn message(&self) -> String { match self.0 { - InnerInvalidEvent::Deserialization { ref error } => error.to_string(), + InnerInvalidEvent::Deserialization { ref error, .. } => error.to_string(), InnerInvalidEvent::Validation { ref message, .. } => message.to_string(), } } - /// The raw `serde_json::Value` representation of the invalid event, if available. + /// The raw event data, if deserialization succeeded but validation failed. + pub fn raw_data(&self) -> Option<&T> { + match self.0 { + InnerInvalidEvent::Validation { ref raw_data, .. } => Some(raw_data), + _ => None, + } + } + + /// The `serde_json::Value` representation of the invalid event, if deserialization failed. pub fn json(&self) -> Option<&Value> { match self.0 { - InnerInvalidEvent::Validation { ref json, .. } => Some(json), + InnerInvalidEvent::Deserialization { ref json, .. } => Some(json), _ => None, } } } -impl Display for InvalidEvent { +impl Display for InvalidEvent { fn fmt(&self, f: &mut Formatter) -> FmtResult { write!(f, "{}", self.message()) } } -impl Error for InvalidEvent {} +impl Error for InvalidEvent {} /// An event that is malformed or otherwise invalid. -#[derive(Debug)] -enum InnerInvalidEvent { +#[derive(Clone, Debug)] +enum InnerInvalidEvent { /// An event that failed to deserialize from JSON. Deserialization { + /// The raw `serde_json::Value` representation of the invalid event. + json: Value, + /// The deserialization error returned by serde. - error: serde_json::Error, + error: String, }, /// An event that deserialized but failed validation. Validation { - /// The raw `serde_json::Value` representation of the invalid event. - json: Value, + /// The event data that failed validation. + raw_data: T, /// An message describing why the event was invalid. message: String, }, } -impl From for InvalidEvent { - fn from(error: serde_json::Error) -> Self { - InvalidEvent(InnerInvalidEvent::Deserialization { error }) - } -} - /// An error returned when attempting to create an event with data that would make it invalid. /// /// This type is similar to `InvalidEvent`, but used during the construction of a new event, as @@ -237,47 +246,64 @@ impl Display for InvalidInput { impl Error for InvalidInput {} +/// Marks types that can be deserialized as EventResult +pub trait EventResultCompatible: Sized { + /// The raw form of this event that deserialization falls back to if deserializing `Self` fails. + type Raw: DeserializeOwned + TryInto; +} + +/// An empty type +#[derive(Debug)] +pub enum Void {} + +impl From for String { + fn from(v: Void) -> Self { + match v {} + } +} + +fn convert_content(res: Raw) -> T +where + Raw: TryInto, +{ + match res.try_into() { + Ok(c) => c, + Err((_, void)) => match void {}, + } +} + /// The result of deserializing an event, which may or may not be valid. /// /// When data is successfully deserialized and validated, this structure will contain the /// deserialized value `T`. When deserialization succeeds, but the event is invalid for any reason, /// this structure will contain an `InvalidEvent`. See the documentation for `InvalidEvent` for /// more details. -#[derive(Debug)] -pub enum EventResult { +#[derive(Clone, Debug)] +pub enum EventResult { /// `T` deserialized and validated successfully. Ok(T), - /// `T` deserialized but was invalid. + /// `T` failed either deserialization or validation. /// - /// `InvalidEvent` contains the original input. - Err(InvalidEvent), + /// `InvalidEvent` contains the error message and the raw data. + Err(InvalidEvent), } -impl EventResult { +impl EventResult { /// Convert `EventResult` into the equivalent `std::result::Result`. - pub fn into_result(self) -> Result { + pub fn into_result(self) -> Result> { match self { EventResult::Ok(t) => Ok(t), EventResult::Err(invalid_event) => Err(invalid_event), } } - - /// Helper for creating a validation error with an error message and the JSON that failed - /// validation. - #[inline] - pub(crate) fn validation_error(message: String, json: serde_json::Value) -> Self { - EventResult::Err(InvalidEvent(InnerInvalidEvent::Validation { - json, - message, - })) - } } -impl<'de, K, V> Deserialize<'de> for EventResult> +impl<'de, T, E> Deserialize<'de> for EventResult where - K: for<'inner> Deserialize<'inner> + Eq + Hash, - V: for<'inner> Deserialize<'inner>, + T: EventResultCompatible, + T::Raw: TryInto, + E: Into, { fn deserialize(deserializer: D) -> Result where @@ -285,19 +311,36 @@ where { let json = serde_json::Value::deserialize(deserializer)?; - let hash_map: HashMap = match serde_json::from_value(json.clone()) { - Ok(hash_map) => hash_map, + let raw_data: T::Raw = match serde_json::from_value(json.clone()) { + Ok(raw) => raw, Err(error) => { return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { + InnerInvalidEvent::Deserialization { json, - message: error.to_string(), + error: error.to_string(), }, ))); } }; - Ok(EventResult::Ok(hash_map)) + match raw_data.try_into() { + Ok(value) => Ok(EventResult::Ok(value)), + Err((raw_data, msg)) => Ok(EventResult::Err(InvalidEvent( + InnerInvalidEvent::Validation { + message: msg.into(), + raw_data, + }, + ))), + } + } +} + +impl Serialize for EventResult { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + unimplemented!() } } @@ -358,29 +401,6 @@ impl<'de> Deserialize<'de> for Empty { } } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let empty: Empty = match serde_json::from_value(json.clone()) { - Ok(empty) => empty, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(empty)) - } -} - /// The type of an event. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum EventType { @@ -523,12 +543,7 @@ pub enum EventType { } /// A basic event. -pub trait Event -where - Self: Debug + Serialize + Sized, - for<'de> EventResult: Deserialize<'de>, - for<'de> EventResult: Deserialize<'de>, -{ +pub trait Event: Debug + Serialize + Sized + EventResultCompatible { /// The type of this event's `content` field. type Content: Debug + Serialize; @@ -540,11 +555,7 @@ where } /// An event within the context of a room. -pub trait RoomEvent: Event -where - for<'de> EventResult: Deserialize<'de>, - for<'de> EventResult<::Content>: Deserialize<'de>, -{ +pub trait RoomEvent: Event { /// The unique identifier for the event. fn event_id(&self) -> &EventId; @@ -566,11 +577,7 @@ where } /// An event that describes persistent state about a room. -pub trait StateEvent: RoomEvent -where - for<'de> EventResult: Deserialize<'de>, - for<'de> EventResult<::Content>: Deserialize<'de>, -{ +pub trait StateEvent: RoomEvent { /// The previous content for this state key, if any. fn prev_content(&self) -> Option<&Self::Content>; @@ -578,56 +585,56 @@ where fn state_key(&self) -> &str; } -// mod custom { -// use ruma_events_macros::ruma_event; -// use serde_json::Value; +mod custom { + use ruma_events_macros::ruma_event; + use serde_json::Value; -// ruma_event! { -// /// A custom basic event not covered by the Matrix specification. -// CustomEvent { -// kind: Event, -// event_type: Custom, -// content_type_alias: { -// /// The payload for `CustomEvent`. -// Value -// }, -// } -// } -// } + ruma_event! { + /// A custom basic event not covered by the Matrix specification. + CustomEvent { + kind: Event, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomEvent`. + Value + }, + } + } +} -// mod custom_room { -// use ruma_events_macros::ruma_event; -// use serde_json::Value; +mod custom_room { + use ruma_events_macros::ruma_event; + use serde_json::Value; -// ruma_event! { -// /// A custom room event not covered by the Matrix specification. -// CustomRoomEvent { -// kind: RoomEvent, -// event_type: Custom, -// content_type_alias: { -// /// The payload for `CustomRoomEvent`. -// Value -// }, -// } -// } -// } + ruma_event! { + /// A custom room event not covered by the Matrix specification. + CustomRoomEvent { + kind: RoomEvent, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomRoomEvent`. + Value + }, + } + } +} -// mod custom_state { -// use ruma_events_macros::ruma_event; -// use serde_json::Value; +mod custom_state { + use ruma_events_macros::ruma_event; + use serde_json::Value; -// ruma_event! { -// /// A custom state event not covered by the Matrix specification. -// CustomStateEvent { -// kind: StateEvent, -// event_type: Custom, -// content_type_alias: { -// /// The payload for `CustomStateEvent`. -// Value -// }, -// } -// } -// } + ruma_event! { + /// A custom state event not covered by the Matrix specification. + CustomStateEvent { + kind: StateEvent, + event_type: Custom, + content_type_alias: { + /// The payload for `CustomStateEvent`. + Value + }, + } + } +} impl Display for EventType { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { @@ -874,6 +881,35 @@ where } } +/// Serde serialization and deserialization functions that map a `Vec` to a +/// +/// To be used as `#[serde(with = "vec_as_map_of_empty")] +mod vec_as_map_of_empty { + use super::Empty; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::{collections::HashMap, hash::Hash}; + + pub fn serialize(vec: &Vec, serializer: S) -> Result + where + S: Serializer, + T: Serialize + Hash + Eq, + { + vec.into_iter() + .map(|v| (v, Empty)) + .collect::>() + .serialize(serializer) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + T: Deserialize<'de> + Hash + Eq, + { + HashMap::::deserialize(deserializer) + .map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect()) + } +} + /// Used to default the `bool` fields to `true` during deserialization. fn default_true() -> bool { true diff --git a/src/macros.rs b/src/macros.rs index b59278a1..b99daa71 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,8 +25,16 @@ macro_rules! impl_enum { } macro_rules! impl_event { - ($name:ident, $content_name:ident, $event_type:path) => { - impl Event for $name { + ($name:ident, $content_name:ident, $event_type:path, $raw_mod:ident) => { + impl crate::EventResultCompatible for $name { + type Raw = $raw_mod::$name; + } + + impl crate::EventResultCompatible for $content_name { + type Raw = $raw_mod::$content_name; + } + + impl crate::Event for $name { /// The type of this event's `content` field. type Content = $content_name; @@ -36,7 +44,7 @@ macro_rules! impl_event { } /// The type of the event. - fn event_type(&self) -> EventType { + fn event_type(&self) -> crate::EventType { $event_type } } @@ -44,8 +52,8 @@ macro_rules! impl_event { } macro_rules! impl_room_event { - ($name:ident, $content_name:ident, $event_type:path) => { - impl_event!($name, $content_name, $event_type); + ($name:ident, $content_name:ident, $event_type:path, $raw_mod:ident) => { + impl_event!($name, $content_name, $event_type, $raw_mod); impl RoomEvent for $name { /// The unique identifier for the event. @@ -81,8 +89,8 @@ macro_rules! impl_room_event { } macro_rules! impl_state_event { - ($name:ident, $content_name:ident, $event_type:path) => { - impl_room_event!($name, $content_name, $event_type); + ($name:ident, $content_name:ident, $event_type:path, $raw_mod:ident) => { + impl_room_event!($name, $content_name, $event_type, $raw_mod); impl StateEvent for $name { /// The previous content for this state key, if any. diff --git a/src/presence.rs b/src/presence.rs index 71951295..0ce86b5b 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -80,6 +80,7 @@ mod tests { use serde_json::to_string; use super::{PresenceEvent, PresenceEventContent, PresenceState}; + use crate::EventResult; #[test] fn serialization() { @@ -118,6 +119,12 @@ mod tests { let json = r#"{"content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"sender":"@example:localhost","type":"m.presence"}"#; - assert_eq!(json.parse::().unwrap(), event); + assert_eq!( + serde_json::from_str::>(json) + .unwrap() + .into_result() + .unwrap(), + event + ); } } diff --git a/src/push_rules.rs b/src/push_rules.rs index 27cc3ca5..a3f9b9c3 100644 --- a/src/push_rules.rs +++ b/src/push_rules.rs @@ -439,6 +439,7 @@ mod tests { Action, EventMatchCondition, PushCondition, PushRulesEvent, RoomMemberCountCondition, SenderNotificationPermissionCondition, Tweak, }; + use crate::EventResult; #[test] fn serialize_string_action() { @@ -776,6 +777,9 @@ mod tests { }, "type": "m.push_rules" }"#; - assert!(json.parse::().is_ok()); + assert!(serde_json::from_str::>(json) + .unwrap() + .into_result() + .is_ok()); } } diff --git a/src/room/canonical_alias.rs b/src/room/canonical_alias.rs index 690fbd89..ea3d4f0f 100644 --- a/src/room/canonical_alias.rs +++ b/src/room/canonical_alias.rs @@ -1,14 +1,13 @@ //! Types for the *m.room.canonical_alias* event. +use std::convert::TryFrom; + use js_int::UInt; use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId}; -use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde_json::Value; -use crate::{ - empty_string_as_none, Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, - RoomEvent, StateEvent, -}; +use crate::{empty_string_as_none, Event, EventType, RoomEvent, StateEvent, Void}; /// Informs the room as to which alias is the canonical one. #[derive(Clone, Debug, PartialEq)] @@ -48,39 +47,28 @@ pub struct CanonicalAliasEventContent { pub alias: Option, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for CanonicalAliasEvent { + type Error = (raw::CanonicalAliasEvent, Void); - let raw: raw::CanonicalAliasEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(CanonicalAliasEvent { - content: CanonicalAliasEventContent { - alias: raw.content.alias, - }, + fn try_from(raw: raw::CanonicalAliasEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, - prev_content: raw - .prev_content - .map(|prev| CanonicalAliasEventContent { alias: prev.alias }), + prev_content: raw.prev_content.map(crate::convert_content), room_id: raw.room_id, sender: raw.sender, state_key: raw.state_key, unsigned: raw.unsigned, - })) + }) + } +} + +impl TryFrom for CanonicalAliasEventContent { + type Error = (raw::CanonicalAliasEventContent, Void); + + fn try_from(raw: raw::CanonicalAliasEventContent) -> Result { + Ok(Self { alias: raw.alias }) } } @@ -132,35 +120,11 @@ impl Serialize for CanonicalAliasEvent { impl_state_event!( CanonicalAliasEvent, CanonicalAliasEventContent, - EventType::RoomCanonicalAlias + EventType::RoomCanonicalAlias, + raw ); -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::CanonicalAliasEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(CanonicalAliasEventContent { - alias: raw.alias, - })) - } -} - -mod raw { +pub(crate) mod raw { use super::*; /// Informs the room as to which alias is the canonical one. @@ -213,7 +177,8 @@ mod tests { use js_int::UInt; use ruma_identifiers::{EventId, RoomAliasId, UserId}; - use super::{CanonicalAliasEvent, CanonicalAliasEventContent, EventResult}; + use super::{CanonicalAliasEvent, CanonicalAliasEventContent}; + use crate::EventResult; #[test] fn serialization_with_optional_fields_as_none() { diff --git a/src/room/create.rs b/src/room/create.rs index 18d39256..59efb291 100644 --- a/src/room/create.rs +++ b/src/room/create.rs @@ -56,6 +56,7 @@ mod tests { use ruma_identifiers::{RoomVersionId, UserId}; use super::CreateEventContent; + use crate::EventResult; #[test] fn serialization() { @@ -82,6 +83,12 @@ mod tests { let json = r#"{"creator":"@carl:example.com","m.federate":true,"room_version":"4"}"#; - assert_eq!(json.parse::().unwrap(), content); + assert_eq!( + serde_json::from_str::>(json) + .unwrap() + .into_result() + .unwrap(), + content + ); } } diff --git a/src/room/encrypted.rs b/src/room/encrypted.rs index b17c0d60..5ce5ccdd 100644 --- a/src/room/encrypted.rs +++ b/src/room/encrypted.rs @@ -1,11 +1,13 @@ //! Types for the *m.room.encrypted* event. +use std::convert::TryFrom; + use js_int::UInt; use ruma_identifiers::{DeviceId, EventId, RoomId, UserId}; use serde::{de::Error, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{from_value, Value}; -use crate::{Algorithm, Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, RoomEvent}; +use crate::{Algorithm, Event, EventType, RoomEvent, Void}; /// This event type is used when sending encrypted events. /// @@ -48,45 +50,34 @@ pub enum EncryptedEventContent { __Nonexhaustive, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for EncryptedEvent { + type Error = (raw::EncryptedEvent, Void); - let raw: raw::EncryptedEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - let content = match raw.content { - raw::EncryptedEventContent::OlmV1Curve25519AesSha2(content) => { - EncryptedEventContent::OlmV1Curve25519AesSha2(content) - } - raw::EncryptedEventContent::MegolmV1AesSha2(content) => { - EncryptedEventContent::MegolmV1AesSha2(content) - } - raw::EncryptedEventContent::__Nonexhaustive => { - panic!("__Nonexhaustive enum variant is not intended for use."); - } - }; - - Ok(EventResult::Ok(EncryptedEvent { - content, + fn try_from(raw: raw::EncryptedEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, room_id: raw.room_id, sender: raw.sender, unsigned: raw.unsigned, - })) + }) + } +} + +impl TryFrom for EncryptedEventContent { + type Error = (raw::EncryptedEventContent, Void); + + fn try_from(raw: raw::EncryptedEventContent) -> Result { + use raw::EncryptedEventContent::*; + + Ok(match raw { + OlmV1Curve25519AesSha2(content) => Self::OlmV1Curve25519AesSha2(content), + MegolmV1AesSha2(content) => Self::MegolmV1AesSha2(content), + __Nonexhaustive => { + unreachable!("__Nonexhaustive variant should be impossible to obtain.") + } + }) } } @@ -129,42 +120,10 @@ impl Serialize for EncryptedEvent { impl_room_event!( EncryptedEvent, EncryptedEventContent, - EventType::RoomEncrypted + EventType::RoomEncrypted, + raw ); -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::EncryptedEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - match raw { - raw::EncryptedEventContent::OlmV1Curve25519AesSha2(content) => Ok(EventResult::Ok( - EncryptedEventContent::OlmV1Curve25519AesSha2(content), - )), - raw::EncryptedEventContent::MegolmV1AesSha2(content) => Ok(EventResult::Ok( - EncryptedEventContent::MegolmV1AesSha2(content), - )), - raw::EncryptedEventContent::__Nonexhaustive => { - panic!("__Nonexhaustive enum variant is not intended for use."); - } - } - } -} - impl Serialize for EncryptedEventContent { fn serialize(&self, serializer: S) -> Result where @@ -180,7 +139,7 @@ impl Serialize for EncryptedEventContent { } } -mod raw { +pub(crate) mod raw { use super::*; /// This event type is used when sending encrypted events. @@ -318,7 +277,8 @@ pub struct MegolmV1AesSha2Content { mod tests { use serde_json::to_string; - use super::{Algorithm, EncryptedEventContent, EventResult, MegolmV1AesSha2Content}; + use super::{Algorithm, EncryptedEventContent, MegolmV1AesSha2Content}; + use crate::EventResult; #[test] fn serializtion() { diff --git a/src/room/message.rs b/src/room/message.rs index 85da80b1..c915ddc8 100644 --- a/src/room/message.rs +++ b/src/room/message.rs @@ -1,5 +1,7 @@ //! Types for the *m.room.message* event. +use std::convert::TryFrom; + use js_int::UInt; use ruma_identifiers::{EventId, RoomId, UserId}; use serde::{ @@ -10,7 +12,7 @@ use serde::{ use serde_json::{from_value, Value}; use super::{EncryptedFile, ImageInfo, ThumbnailInfo}; -use crate::{Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, RoomEvent}; +use crate::{Event, EventType, RoomEvent, Void}; pub mod feedback; @@ -74,50 +76,41 @@ pub enum MessageEventContent { __Nonexhaustive, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for MessageEvent { + type Error = (raw::MessageEvent, Void); - let raw: raw::MessageEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(MessageEvent { - content: match raw.content { - raw::MessageEventContent::Audio(content) => MessageEventContent::Audio(content), - raw::MessageEventContent::Emote(content) => MessageEventContent::Emote(content), - raw::MessageEventContent::File(content) => MessageEventContent::File(content), - raw::MessageEventContent::Image(content) => MessageEventContent::Image(content), - raw::MessageEventContent::Location(content) => { - MessageEventContent::Location(content) - } - raw::MessageEventContent::Notice(content) => MessageEventContent::Notice(content), - raw::MessageEventContent::ServerNotice(content) => { - MessageEventContent::ServerNotice(content) - } - raw::MessageEventContent::Text(content) => MessageEventContent::Text(content), - raw::MessageEventContent::Video(content) => MessageEventContent::Video(content), - raw::MessageEventContent::__Nonexhaustive => { - panic!("__Nonexhaustive enum variant is not intended for use.") - } - }, + fn try_from(raw: raw::MessageEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, room_id: raw.room_id, sender: raw.sender, unsigned: raw.unsigned, - })) + }) + } +} + +impl TryFrom for MessageEventContent { + type Error = (raw::MessageEventContent, Void); + + fn try_from(raw: raw::MessageEventContent) -> Result { + use raw::MessageEventContent::*; + + Ok(match raw { + Audio(content) => Self::Audio(content), + Emote(content) => Self::Emote(content), + File(content) => Self::File(content), + Image(content) => Self::Image(content), + Location(content) => Self::Location(content), + Notice(content) => Self::Notice(content), + ServerNotice(content) => Self::ServerNotice(content), + Text(content) => Self::Text(content), + Video(content) => Self::Video(content), + __Nonexhaustive => { + unreachable!("It should be impossible to obtain a __Nonexhaustive variant.") + } + }) } } @@ -157,7 +150,12 @@ impl Serialize for MessageEvent { } } -impl_room_event!(MessageEvent, MessageEventContent, EventType::RoomMessage); +impl_room_event!( + MessageEvent, + MessageEventContent, + EventType::RoomMessage, + raw +); impl Serialize for MessageEventContent { fn serialize(&self, serializer: S) -> Result @@ -181,47 +179,7 @@ impl Serialize for MessageEventContent { } } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::MessageEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - let content = match raw { - raw::MessageEventContent::Audio(content) => MessageEventContent::Audio(content), - raw::MessageEventContent::Emote(content) => MessageEventContent::Emote(content), - raw::MessageEventContent::File(content) => MessageEventContent::File(content), - raw::MessageEventContent::Image(content) => MessageEventContent::Image(content), - raw::MessageEventContent::Location(content) => MessageEventContent::Location(content), - raw::MessageEventContent::Notice(content) => MessageEventContent::Notice(content), - raw::MessageEventContent::ServerNotice(content) => { - MessageEventContent::ServerNotice(content) - } - raw::MessageEventContent::Text(content) => MessageEventContent::Text(content), - raw::MessageEventContent::Video(content) => MessageEventContent::Video(content), - raw::MessageEventContent::__Nonexhaustive => { - panic!("__Nonexhaustive enum variant is not intended for use.") - } - }; - - Ok(EventResult::Ok(content)) - } -} - -mod raw { +pub(crate) mod raw { use super::*; /// A message sent to a room. @@ -1085,7 +1043,8 @@ impl Serialize for VideoMessageEventContent { mod tests { use serde_json::to_string; - use super::{AudioMessageEventContent, EventResult, MessageEventContent}; + use super::{AudioMessageEventContent, MessageEventContent}; + use crate::EventResult; #[test] fn serialization() { diff --git a/src/room/name.rs b/src/room/name.rs index 28ce50a5..03e71204 100644 --- a/src/room/name.rs +++ b/src/room/name.rs @@ -1,13 +1,14 @@ //! Types for the *m.room.name* event. +use std::convert::TryFrom; + use js_int::UInt; use ruma_identifiers::{EventId, RoomId, UserId}; -use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde_json::Value; use crate::{ - empty_string_as_none, Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, - InvalidInput, RoomEvent, StateEvent, + empty_string_as_none, Event as _, EventType, InvalidInput, RoomEvent, StateEvent, Void, }; /// A human-friendly room name designed to be displayed to the end-user. @@ -46,39 +47,28 @@ pub struct NameEventContent { pub(crate) name: Option, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for NameEvent { + type Error = (raw::NameEvent, Void); - let raw: raw::NameEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(NameEvent { - content: NameEventContent { - name: raw.content.name, - }, + fn try_from(raw: raw::NameEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, - prev_content: raw - .prev_content - .map(|prev| NameEventContent { name: prev.name }), + prev_content: raw.prev_content.map(crate::convert_content), room_id: raw.room_id, sender: raw.sender, state_key: raw.state_key, unsigned: raw.unsigned, - })) + }) + } +} + +impl TryFrom for NameEventContent { + type Error = (raw::NameEventContent, Void); + + fn try_from(raw: raw::NameEventContent) -> Result { + Ok(Self { name: raw.name }) } } @@ -127,7 +117,7 @@ impl Serialize for NameEvent { } } -impl_state_event!(NameEvent, NameEventContent, EventType::RoomName); +impl_state_event!(NameEvent, NameEventContent, EventType::RoomName, raw); impl NameEventContent { /// Create a new `NameEventContent` with the given name. @@ -151,30 +141,7 @@ impl NameEventContent { } } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::NameEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(NameEventContent { name: raw.name })) - } -} - -mod raw { +pub(crate) mod raw { use super::*; /// A human-friendly room name designed to be displayed to the end-user. @@ -226,7 +193,8 @@ mod tests { use ruma_identifiers::{EventId, RoomId, UserId}; use serde_json::Value; - use super::{EventResult, NameEvent, NameEventContent}; + use super::{NameEvent, NameEventContent}; + use crate::EventResult; #[test] fn serialization_with_optional_fields_as_none() { diff --git a/src/room/pinned_events.rs b/src/room/pinned_events.rs index 48e8a408..2fa88758 100644 --- a/src/room/pinned_events.rs +++ b/src/room/pinned_events.rs @@ -25,7 +25,7 @@ mod tests { use crate::{ room::pinned_events::{PinnedEventsEvent, PinnedEventsEventContent}, - Event, RoomEvent, StateEvent, + Event, EventResult, RoomEvent, StateEvent, }; #[test] @@ -47,7 +47,11 @@ mod tests { }; let serialized_event = to_string(&event).unwrap(); - let parsed_event: PinnedEventsEvent = serialized_event.parse().unwrap(); + let parsed_event: PinnedEventsEvent = + serde_json::from_str::>(&serialized_event) + .unwrap() + .into_result() + .unwrap(); assert_eq!(parsed_event.event_id(), event.event_id()); assert_eq!(parsed_event.room_id(), event.room_id()); diff --git a/src/room/power_levels.rs b/src/room/power_levels.rs index f374ffdf..e1d5f573 100644 --- a/src/room/power_levels.rs +++ b/src/room/power_levels.rs @@ -1,15 +1,13 @@ //! Types for the *m.room.power_levels* event. -use std::collections::HashMap; +use std::{collections::HashMap, convert::TryFrom}; use js_int::{Int, UInt}; use ruma_identifiers::{EventId, RoomId, UserId}; -use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde_json::Value; -use crate::{ - Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, RoomEvent, StateEvent, -}; +use crate::{Event, EventType, RoomEvent, StateEvent, Void}; /// Defines the power levels (privileges) of users in the room. #[derive(Clone, Debug, PartialEq)] @@ -87,57 +85,39 @@ pub struct PowerLevelsEventContent { pub notifications: NotificationPowerLevels, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for PowerLevelsEvent { + type Error = (raw::PowerLevelsEvent, Void); - let raw: raw::PowerLevelsEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(PowerLevelsEvent { - content: PowerLevelsEventContent { - ban: raw.content.ban, - events: raw.content.events, - events_default: raw.content.events_default, - invite: raw.content.invite, - kick: raw.content.kick, - redact: raw.content.redact, - state_default: raw.content.state_default, - users: raw.content.users, - users_default: raw.content.users_default, - notifications: raw.content.notifications, - }, + fn try_from(raw: raw::PowerLevelsEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, - prev_content: raw.prev_content.map(|prev| PowerLevelsEventContent { - ban: prev.ban, - events: prev.events, - events_default: prev.events_default, - invite: prev.invite, - kick: prev.kick, - redact: prev.redact, - state_default: prev.state_default, - users: prev.users, - users_default: prev.users_default, - notifications: prev.notifications, - }), + prev_content: raw.prev_content.map(crate::convert_content), room_id: raw.room_id, + unsigned: raw.unsigned, sender: raw.sender, state_key: raw.state_key, - unsigned: raw.unsigned, - })) + }) + } +} + +impl TryFrom for PowerLevelsEventContent { + type Error = (raw::PowerLevelsEventContent, Void); + + fn try_from(raw: raw::PowerLevelsEventContent) -> Result { + Ok(Self { + ban: raw.ban, + events: raw.events, + events_default: raw.events_default, + invite: raw.invite, + kick: raw.kick, + redact: raw.redact, + state_default: raw.state_default, + users: raw.users, + users_default: raw.users_default, + notifications: raw.notifications, + }) } } @@ -189,44 +169,11 @@ impl Serialize for PowerLevelsEvent { impl_state_event!( PowerLevelsEvent, PowerLevelsEventContent, - EventType::RoomPowerLevels + EventType::RoomPowerLevels, + raw ); -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::PowerLevelsEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(PowerLevelsEventContent { - ban: raw.ban, - events: raw.events, - events_default: raw.events_default, - invite: raw.invite, - kick: raw.kick, - redact: raw.redact, - state_default: raw.state_default, - users: raw.users, - users_default: raw.users_default, - notifications: raw.notifications, - })) - } -} - -mod raw { +pub(crate) mod raw { use super::*; /// Defines the power levels (privileges) of users in the room. diff --git a/src/room/server_acl.rs b/src/room/server_acl.rs index 34f82611..f98cee82 100644 --- a/src/room/server_acl.rs +++ b/src/room/server_acl.rs @@ -1,14 +1,13 @@ //! Types for the *m.room.server_acl* event. +use std::convert::TryFrom; + use js_int::UInt; use ruma_identifiers::{EventId, RoomId, UserId}; -use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde_json::Value; -use crate::{ - default_true, Event, EventResult, EventType, InnerInvalidEvent, InvalidEvent, RoomEvent, - StateEvent, -}; +use crate::{default_true, Event as _, EventType, RoomEvent, StateEvent, Void}; /// An event to indicate which servers are permitted to participate in the room. #[derive(Clone, Debug, PartialEq)] @@ -68,43 +67,32 @@ pub struct ServerAclEventContent { pub deny: Vec, } -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; +impl TryFrom for ServerAclEvent { + type Error = (raw::ServerAclEvent, Void); - let raw: raw::ServerAclEvent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(ServerAclEvent { - content: ServerAclEventContent { - allow_ip_literals: raw.content.allow_ip_literals, - allow: raw.content.allow, - deny: raw.content.deny, - }, + fn try_from(raw: raw::ServerAclEvent) -> Result { + Ok(Self { + content: crate::convert_content(raw.content), event_id: raw.event_id, origin_server_ts: raw.origin_server_ts, - prev_content: raw.prev_content.map(|prev| ServerAclEventContent { - allow_ip_literals: prev.allow_ip_literals, - allow: prev.allow, - deny: prev.deny, - }), + prev_content: raw.prev_content.map(crate::convert_content), room_id: raw.room_id, - unsigned: raw.unsigned, sender: raw.sender, state_key: raw.state_key, - })) + unsigned: raw.unsigned, + }) + } +} + +impl TryFrom for ServerAclEventContent { + type Error = (raw::ServerAclEventContent, Void); + + fn try_from(raw: raw::ServerAclEventContent) -> Result { + Ok(Self { + allow_ip_literals: raw.allow_ip_literals, + allow: raw.allow, + deny: raw.deny, + }) } } @@ -125,37 +113,11 @@ impl Serialize for ServerAclEvent { impl_state_event!( ServerAclEvent, ServerAclEventContent, - EventType::RoomServerAcl + EventType::RoomServerAcl, + raw ); -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let json = serde_json::Value::deserialize(deserializer)?; - - let raw: raw::ServerAclEventContent = match serde_json::from_value(json.clone()) { - Ok(raw) => raw, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent( - InnerInvalidEvent::Validation { - json, - message: error.to_string(), - }, - ))); - } - }; - - Ok(EventResult::Ok(ServerAclEventContent { - allow_ip_literals: raw.allow_ip_literals, - allow: raw.allow, - deny: raw.deny, - })) - } -} - -mod raw { +pub(crate) mod raw { use super::*; /// An event to indicate which servers are permitted to participate in the room. @@ -221,12 +183,13 @@ mod raw { #[cfg(test)] mod tests { use super::ServerAclEvent; + use crate::EventResult; #[test] fn default_values() { let server_acl_event: ServerAclEvent = - r#"{"content":{},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"sender":"@carl:example.com","state_key":"","type":"m.room.server_acl"}"# - .parse().unwrap(); + serde_json::from_str::>(r#"{"content":{},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"sender":"@carl:example.com","state_key":"","type":"m.room.server_acl"}"#) + .unwrap().into_result().unwrap(); assert_eq!(server_acl_event.content.allow_ip_literals, true); assert!(server_acl_event.content.allow.is_empty()); diff --git a/src/stripped.rs b/src/stripped.rs index dcfadb28..bb5d1348 100644 --- a/src/stripped.rs +++ b/src/stripped.rs @@ -8,8 +8,7 @@ use std::convert::TryFrom; use ruma_identifiers::UserId; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::from_value; +use serde::{Serialize, Serializer}; use crate::{ room::{ @@ -20,7 +19,7 @@ use crate::{ power_levels::PowerLevelsEventContent, third_party_invite::ThirdPartyInviteEventContent, topic::TopicEventContent, }, - EventResult, EventType, InnerInvalidEvent, InvalidEvent, + EventResultCompatible, EventType, }; /// A stripped-down version of a state event that is included along with some other events. @@ -117,194 +116,43 @@ pub type StrippedRoomThirdPartyInvite = StrippedStateContent; -impl<'de> Deserialize<'de> for EventResult { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value = serde_json::Value::deserialize(deserializer)?; +impl EventResultCompatible for StrippedState { + type Raw = raw::StrippedState; +} - let event_type_value = match value.get("type") { - Some(value) => value.clone(), - None => { - return Ok(EventResult::Err(InvalidEvent(InnerInvalidEvent::Validation { - json: value, - message: "missing field `type`".to_string(), - }))) - } - }; +impl TryFrom for StrippedState { + type Error = (raw::StrippedState, String); - let event_type = match from_value::(event_type_value.clone()) { - Ok(event_type) => event_type, - Err(error) => { - return Ok(EventResult::Err(InvalidEvent(InnerInvalidEvent::Validation { - json: value, - message: error.to_string(), - }))) - } - }; - - let content = match value.get("content") { - Some(content_value) => content_value, - None => { - return Ok(EventResult::validation_error("missing field `content`".to_string(), value)) - } - }; - - let stripped_state = match event_type { - // TODO: On the next stream, start with doing the other variants in this match. - EventType::RoomAliases => { - let content_result = match from_value::>(content.clone()) { - Ok(content_result) => content_result, - Err(error) => return Err(D::Error::custom(error)), - }; - - let content = match content_result { - EventResult::Ok(content) => content, - EventResult::Err(error) => return Ok(EventResult::Err(error)), - }; - - StrippedState::RoomAliases(StrippedStateContent { - content, - event_type, - state_key: match value.get("state_key") { - Some(state_key_value) => match state_key_value.as_str() { - Some(state_key) => state_key.to_string(), - None => { - return Ok(EventResult::validation_error("field `state_key` must be a string".to_string(), value)); - } - }, - None => { - return Ok(EventResult::validation_error("missing field `state_key`".to_string(), value)); - } - }, - sender: match value.get("sender") { - Some(sender_value) => match sender_value.as_str() { - Some(sender_str) => match UserId::try_from(sender_str) { - Ok(sender) => sender, - Err(error) => { - return Ok(EventResult::validation_error(error.to_string(), value)); - } - }, - None => { - return Ok(EventResult::validation_error("field `sender` must be a string".to_string(), value)); - } - }, - None => { - return Ok(EventResult::validation_error("missing field `sender`".to_string(), value)); - } - }, - }) - } - // EventType::RoomAvatar => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomAvatar(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomCanonicalAlias => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomCanonicalAlias(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomCreate => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomCreate(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomGuestAccess => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomGuestAccess(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomHistoryVisibility => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomHistoryVisibility(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomJoinRules => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomJoinRules(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomMember => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomMember(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomName => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomName(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomPowerLevels => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomPowerLevels(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomThirdPartyInvite => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomThirdPartyInvite(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - // EventType::RoomTopic => match from_value::(value) { - // Ok(stripped_state) => StrippedState::RoomTopic(stripped_state), - // Err(error) => { - // return Ok(EventResult::Ok(InvalidEvent(InnerInvalidEvent::Validation { - // json: value, - // message: error.to_string(), - // }))) - // } - // }, - _ => return Ok(EventResult::Err(InvalidEvent(InnerInvalidEvent::Validation { - json: value, - message: "not a state event".to_string(), - }))), - }; - - Ok(EventResult::Ok(stripped_state)) + fn try_from(raw: raw::StrippedState) -> Result { + unimplemented!() } } +/*impl EventResultCompatible for StrippedStateContent +where + C: EventResultCompatible, +{ + type Raw = StrippedStateContent; +} + +// Orphan impl :( +impl TryFrom> for StrippedStateContent +where + C: EventResultCompatible, + C::Raw: TryInto, +{ + type Error = (StrippedStateContent, E); + + fn try_from(raw: StrippedStateContent) -> Result { + Ok(Self { + content: raw.content.try_into()?, + event_type: raw.event_type, + state_key: raw.state_key, + sender: raw.sender, + }) + } +}*/ + impl Serialize for StrippedState { fn serialize(&self, serializer: S) -> Result where @@ -327,109 +175,106 @@ impl Serialize for StrippedState { } } -// impl<'de, C> Deserialize<'de> for EventResult> -// where -// EventResult: Deserialize<'de>, -// { -// fn deserialize(deserializer: D) -> Result -// where -// D: Deserializer<'de>, -// { -// let value = serde_json::Value::deserialize(deserializer)?; +mod raw { + use serde::{Deserialize, Deserializer}; -// let event_type_value = match value.get("type") { -// Some(value) => value.clone(), -// None => { -// return Ok(EventResult::validation_error("missing field `type`".to_string(), value)) -// } -// }; + use super::StrippedStateContent; + use crate::room::{ + aliases::raw::AliasesEventContent, avatar::raw::AvatarEventContent, + canonical_alias::raw::CanonicalAliasEventContent, create::raw::CreateEventContent, + guest_access::raw::GuestAccessEventContent, + history_visibility::raw::HistoryVisibilityEventContent, + join_rules::raw::JoinRulesEventContent, member::raw::MemberEventContent, + name::raw::NameEventContent, power_levels::raw::PowerLevelsEventContent, + third_party_invite::raw::ThirdPartyInviteEventContent, topic::raw::TopicEventContent, + }; -// let event_type = match from_value::(event_type_value.clone()) { -// Ok(event_type) => event_type, -// Err(error) => { -// return Ok(EventResult::validation_error(error.to_string(), value)) -// } -// }; + /// A stripped-down version of the *m.room.aliases* event. + pub type StrippedRoomAliases = StrippedStateContent; -// let content = match value.get("content") { -// Some(content_value) => match content_value.as_object() { -// Some(content) => content, -// None => { -// return Ok(EventResult::validation_error("field `content` must be an object".to_string(), value)) -// } -// }, -// None => { -// return Ok(EventResult::validation_error("missing field `content`".to_string(), value)) -// } -// }; + /// A stripped-down version of the *m.room.avatar* event. + pub type StrippedRoomAvatar = StrippedStateContent; -// match event_type { -// EventType::RoomAliases => stripped_state_content::(event_type, value), -// EventType::RoomAvatar => stripped_state_content(event_type, value), -// EventType::RoomCanonicalAlias => { -// stripped_state_content(event_type, value) -// } -// EventType::RoomCreate => stripped_state_content(event_type, value), -// EventType::RoomGuestAccess => stripped_state_content(event_type, value), -// EventType::RoomHistoryVisibility => { -// stripped_state_content(event_type, value) -// } -// EventType::RoomJoinRules => stripped_state_content(event_type, value), -// EventType::RoomMember => stripped_state_content(event_type, value), -// EventType::RoomName => stripped_state_content(event_type, value), -// EventType::RoomPowerLevels => stripped_state_content(event_type, value), -// EventType::RoomThirdPartyInvite => { -// stripped_state_content(event_type, value) -// } -// EventType::RoomTopic => stripped_state_content(event_type, value), -// _ => Ok(EventResult::validation_error("not a state event".to_string(), value)), -// } -// } -// } + /// A stripped-down version of the *m.room.canonical_alias* event. + pub type StrippedRoomCanonicalAlias = StrippedStateContent; -// /// Reduces the boilerplate in the match arms of `impl Deserialize for StrippedState`. -// #[inline] -// fn create_stripped_state( -// event_type: EventType, -// value: Value, -// ) -> Result, serde_json::Error> -// where -// for<'de> EventResult: Deserialize<'de>, -// { -// let event_result = from_value::>(value)?; + /// A stripped-down version of the *m.room.create* event. + pub type StrippedRoomCreate = StrippedStateContent; -// Ok(EventResult::Ok(StrippedStateContent { -// content: event_result.into_result().unwrap(), -// event_type, -// state_key: match value.get("state_key") { -// Some(state_key_value) => match state_key_value.as_str() { -// Some(state_key) => state_key.to_string(), -// None => { -// return Ok(EventResult::validation_error("field `state_key` must be a string".to_string(), value)); -// } -// }, -// None => { -// return Ok(EventResult::validation_error("missing field `state_key`".to_string(), value)); -// } -// }, -// sender: match value.get("sender") { -// Some(sender_value) => match sender_value.as_str() { -// Some(sender_str) => match UserId::try_from(sender_str) { -// Ok(sender) => sender, -// Err(error) => { -// return Ok(EventResult::validation_error(error.to_string(), value)); -// } -// }, -// None => { -// return Ok(EventResult::validation_error("field `sender` must be a string".to_string(), value)); -// } -// }, -// None => { -// return Ok(EventResult::validation_error("missing field `sender`".to_string(), value)); -// } -// }, -// })) -// } + /// A stripped-down version of the *m.room.guest_access* event. + pub type StrippedRoomGuestAccess = StrippedStateContent; + + /// A stripped-down version of the *m.room.history_visibility* event. + pub type StrippedRoomHistoryVisibility = StrippedStateContent; + + /// A stripped-down version of the *m.room.join_rules* event. + pub type StrippedRoomJoinRules = StrippedStateContent; + + /// A stripped-down version of the *m.room.member* event. + pub type StrippedRoomMember = StrippedStateContent; + + /// A stripped-down version of the *m.room.name* event. + pub type StrippedRoomName = StrippedStateContent; + + /// A stripped-down version of the *m.room.power_levels* event. + pub type StrippedRoomPowerLevels = StrippedStateContent; + + /// A stripped-down version of the *m.room.third_party_invite* event. + pub type StrippedRoomThirdPartyInvite = StrippedStateContent; + + /// A stripped-down version of the *m.room.topic* event. + pub type StrippedRoomTopic = StrippedStateContent; + + /// A stripped-down version of a state event that is included along with some other events. + #[derive(Clone, Debug)] + #[allow(clippy::large_enum_variant)] + pub enum StrippedState { + /// A stripped-down version of the *m.room.aliases* event. + RoomAliases(StrippedRoomAliases), + + /// A stripped-down version of the *m.room.avatar* event. + RoomAvatar(StrippedRoomAvatar), + + /// A stripped-down version of the *m.room.canonical_alias* event. + RoomCanonicalAlias(StrippedRoomCanonicalAlias), + + /// A striped-down version of the *m.room.create* event. + RoomCreate(StrippedRoomCreate), + + /// A stripped-down version of the *m.room.guest_access* event. + RoomGuestAccess(StrippedRoomGuestAccess), + + /// A stripped-down version of the *m.room.history_visibility* event. + RoomHistoryVisibility(StrippedRoomHistoryVisibility), + + /// A stripped-down version of the *m.room.join_rules* event. + RoomJoinRules(StrippedRoomJoinRules), + + /// A stripped-down version of the *m.room.member* event. + RoomMember(StrippedRoomMember), + + /// A stripped-down version of the *m.room.name* event. + RoomName(StrippedRoomName), + + /// A stripped-down version of the *m.room.power_levels* event. + RoomPowerLevels(StrippedRoomPowerLevels), + + /// A stripped-down version of the *m.room.third_party_invite* event. + RoomThirdPartyInvite(StrippedRoomThirdPartyInvite), + + /// A stripped-down version of the *m.room.topic* event. + RoomTopic(StrippedRoomTopic), + } + + impl<'de> Deserialize<'de> for StrippedState { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + unimplemented!() + } + } +} #[cfg(test)] mod tests { @@ -442,7 +287,7 @@ mod tests { use super::{StrippedRoomName, StrippedRoomTopic, StrippedState}; use crate::{ room::{join_rules::JoinRule, topic::TopicEventContent}, - EventType, + EventResult, EventType, }; #[test] @@ -509,7 +354,11 @@ mod tests { } }"#; - match name_event.parse().unwrap() { + match serde_json::from_str::>(name_event) + .unwrap() + .into_result() + .unwrap() + { StrippedState::RoomName(event) => { assert_eq!(event.content.name, Some("Ruma".to_string())); assert_eq!(event.event_type, EventType::RoomName); @@ -520,9 +369,18 @@ mod tests { }; // Ensure `StrippedStateContent` can be parsed, not just `StrippedState`. - assert!(name_event.parse::().is_ok()); + /*assert!( + serde_json::from_str::>(name_event) + .unwrap() + .into_result() + .is_ok() + );*/ - match join_rules_event.parse().unwrap() { + match serde_json::from_str::>(join_rules_event) + .unwrap() + .into_result() + .unwrap() + { StrippedState::RoomJoinRules(event) => { assert_eq!(event.content.join_rule, JoinRule::Public); assert_eq!(event.event_type, EventType::RoomJoinRules); @@ -532,7 +390,11 @@ mod tests { _ => unreachable!(), }; - match avatar_event.parse().unwrap() { + match serde_json::from_str::>(avatar_event) + .unwrap() + .into_result() + .unwrap() + { StrippedState::RoomAvatar(event) => { let image_info = event.content.info.unwrap();