//! Crate ruma_events contains serializable types for the events in the [Matrix](https://matrix.org) //! specification that can be shared by client and server code. #![feature(custom_derive, plugin, question_mark)] #![plugin(serde_macros)] #![deny(missing_docs)] extern crate ruma_identifiers; extern crate serde; extern crate serde_json; use std::fmt::{Display, Formatter, Error as FmtError}; use std::marker::PhantomData; use std::str::FromStr; use ruma_identifiers::{EventId, RoomId, UserId}; use serde::{Deserialize, Deserializer, Error as SerdeError, Serialize, Serializer}; use serde::de::Visitor as SerdeVisitor; use serde_json::Value; #[macro_use] mod macros; pub mod call; pub mod presence; pub mod receipt; pub mod room; pub mod stripped; pub mod tag; pub mod typing; /// The type of an event. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum EventType { /// m.call.answer CallAnswer, /// m.call.candidates CallCandidates, /// m.call.hangup CallHangup, /// m.call.invite CallInvite, /// m.presence Presence, /// m.receipt Receipt, /// m.room.aliases RoomAliases, /// m.room.avatar RoomAvatar, /// m.room.canonical_alias RoomCanonicalAlias, /// m.room.create RoomCreate, /// m.room.guest_access RoomGuestAccess, /// m.room.history_visibility RoomHistoryVisibility, /// m.room.join_rules RoomJoinRules, /// m.room.member RoomMember, /// m.room.message RoomMessage, /// m.room.name RoomName, /// m.room.power_levels RoomPowerLevels, /// m.room.redaction RoomRedaction, /// m.room.third_party_invite RoomThirdPartyInvite, /// m.room.topic RoomTopic, /// m.tag Tag, /// m.typing Typing, /// Any event that is not part of the specification. Custom(String), } /// A basic event. #[derive(Debug, Deserialize, Serialize)] pub struct Event where C: Deserialize + Serialize, E: Deserialize + Serialize { /// Data specific to the event type. pub content: C, /// The type of the event. #[serde(rename="type")] pub event_type: EventType, /// Extra top-level key-value pairs specific to this event type, but that are not under the /// `content` field. pub extra_content: E, } /// An event within the context of a room. #[derive(Debug, Deserialize, Serialize)] pub struct RoomEvent where C: Deserialize + Serialize, E: Deserialize + Serialize { /// Data specific to the event type. pub content: C, /// The unique identifier for the event. pub event_id: EventId, /// Extra top-level key-value pairs specific to this event type, but that are not under the /// `content` field. pub extra_content: E, /// The type of the event. #[serde(rename="type")] pub event_type: EventType, /// The unique identifier for the room associated with this event. pub room_id: RoomId, /// Additional key-value pairs not signed by the homeserver. pub unsigned: Option, /// The unique identifier for the user associated with this event. #[serde(rename="sender")] pub user_id: UserId, } /// An event that describes persistent state about a room. #[derive(Debug, Deserialize, Serialize)] pub struct StateEvent where C: Deserialize + Serialize, E: Deserialize + Serialize { /// Data specific to the event type. pub content: C, /// The unique identifier for the event. pub event_id: EventId, /// The type of the event. #[serde(rename="type")] pub event_type: EventType, /// Extra top-level key-value pairs specific to this event type, but that are not under the /// `content` field. pub extra_content: E, /// The previous content for this state key, if any. pub prev_content: Option, /// The unique identifier for the room associated with this event. pub room_id: RoomId, /// A key that determines which piece of room state the event represents. pub state_key: String, /// Additional key-value pairs not signed by the homeserver. pub unsigned: Option, /// The unique identifier for the user associated with this event. #[serde(rename="sender")] pub user_id: UserId, } /// An error when attempting to convert a string to an enum that only accepts certain values. pub struct ParseError; /// A Serde `Visitor` for deserializing various ruma-events enums. struct Visitor where T: Deserialize + FromStr { _phantom: PhantomData, } impl Display for EventType { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { let event_type_str = match *self { EventType::CallAnswer => "m.call.answer", EventType::CallCandidates => "m.call.candidates", EventType::CallHangup => "m.call.hangup", EventType::CallInvite => "m.call.invite", EventType::Presence => "m.presence", EventType::Receipt => "m.receipt", EventType::RoomAliases => "m.room.aliases", EventType::RoomAvatar => "m.room.avatar", EventType::RoomCanonicalAlias => "m.room.canonical_alias", EventType::RoomCreate => "m.room.create", EventType::RoomGuestAccess => "m.room.guest_access", EventType::RoomHistoryVisibility => "m.room.history_visibility", EventType::RoomJoinRules => "m.room.join_rules", EventType::RoomMember => "m.room.member", EventType::RoomMessage => "m.room.message", EventType::RoomName => "m.room.name", EventType::RoomPowerLevels => "m.room.power_levels", EventType::RoomRedaction => "m.room.redaction", EventType::RoomThirdPartyInvite => "m.room.third_party_invite", EventType::RoomTopic => "m.room.topic", EventType::Tag => "m.tag", EventType::Typing => "m.typing", EventType::Custom(ref event_type) => event_type, }; write!(f, "{}", event_type_str) } } impl<'a> From<&'a str> for EventType { fn from(s: &'a str) -> EventType { match s { "m.call.answer" => EventType::CallAnswer, "m.call.candidates" => EventType::CallCandidates, "m.call.hangup" => EventType::CallHangup, "m.call.invite" => EventType::CallInvite, "m.presence" => EventType::Presence, "m.receipt" => EventType::Receipt, "m.room.aliases" => EventType::RoomAliases, "m.room.avatar" => EventType::RoomAvatar, "m.room.canonical_alias" => EventType::RoomCanonicalAlias, "m.room.create" => EventType::RoomCreate, "m.room.guest_access" => EventType::RoomGuestAccess, "m.room.history_visibility" => EventType::RoomHistoryVisibility, "m.room.join_rules" => EventType::RoomJoinRules, "m.room.member" => EventType::RoomMember, "m.room.message" => EventType::RoomMessage, "m.room.name" => EventType::RoomName, "m.room.power_levels" => EventType::RoomPowerLevels, "m.room.redaction" => EventType::RoomRedaction, "m.room.third_party_invite" => EventType::RoomThirdPartyInvite, "m.room.topic" => EventType::RoomTopic, "m.tag" => EventType::Tag, "m.typing" => EventType::Typing, event_type => EventType::Custom(event_type.to_string()), } } } impl Serialize for EventType { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { serializer.serialize_str(&self.to_string()) } } impl Deserialize for EventType { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { struct EventTypeVisitor; impl SerdeVisitor for EventTypeVisitor { type Value = EventType; fn visit_str(&mut self, v: &str) -> Result where E: SerdeError { Ok(EventType::from(v)) } } deserializer.deserialize_str(EventTypeVisitor) } } impl Visitor where T: Deserialize + FromStr { pub fn new() -> Visitor { Visitor { _phantom: PhantomData, } } } impl SerdeVisitor for Visitor where T: Deserialize + FromStr { type Value = T; fn visit_str(&mut self, v: &str) -> Result where E: SerdeError { v.parse().map_err(|_| { E::invalid_value(v) }) } } #[cfg(test)] mod tests { use serde_json::{from_str, to_string}; use super::EventType; #[test] fn event_types_serialize_to_display_form() { assert_eq!( to_string(&EventType::RoomCreate).unwrap(), r#""m.room.create""# ); } #[test] fn custom_event_types_serialize_to_display_form() { assert_eq!( to_string(&EventType::Custom("io.ruma.test".to_string())).unwrap(), r#""io.ruma.test""# ); } #[test] fn event_types_deserialize_from_display_form() { assert_eq!( from_str::(r#""m.room.create""#).unwrap(), EventType::RoomCreate ); } #[test] fn custom_event_types_deserialize_from_display_form() { assert_eq!( from_str::(r#""io.ruma.test""#).unwrap(), EventType::Custom("io.ruma.test".to_string()) ) } }