diff --git a/src/call/answer.rs b/src/call/answer.rs index a41ba314..7ec22242 100644 --- a/src/call/answer.rs +++ b/src/call/answer.rs @@ -10,7 +10,7 @@ room_event! { /// The payload of an `AnswerEvent`. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AnswerEventContent { - /// The VoIP session description. + /// The VoIP session description object. The session description type must be *answer*. pub answer: SessionDescription, /// The ID of the call this event relates to. pub call_id: String, diff --git a/src/call/invite.rs b/src/call/invite.rs index e6662c7b..5493a836 100644 --- a/src/call/invite.rs +++ b/src/call/invite.rs @@ -16,7 +16,7 @@ pub struct InviteEventContent { /// value, clients should discard it. They should also no longer show the call as awaiting an /// answer in the UI. pub lifetime: u64, - /// The session description object. + /// The session description object. The session description type must be *offer*. pub offer: SessionDescription, /// The version of the VoIP specification this messages adheres to. pub version: u64, diff --git a/src/collections/all.rs b/src/collections/all.rs index 9343cc28..a1a3b53f 100644 --- a/src/collections/all.rs +++ b/src/collections/all.rs @@ -6,6 +6,7 @@ use call::answer::AnswerEvent; use call::candidates::CandidatesEvent; use call::hangup::HangupEvent; use call::invite::InviteEvent; +use direct::DirectEvent; use presence::PresenceEvent; use receipt::ReceiptEvent; use room::aliases::AliasesEvent; @@ -18,6 +19,7 @@ use room::join_rules::JoinRulesEvent; use room::member::MemberEvent; use room::message::MessageEvent; use room::name::NameEvent; +use room::pinned_events::PinnedEventsEvent; use room::power_levels::PowerLevelsEvent; use room::redaction::RedactionEvent; use room::third_party_invite::ThirdPartyInviteEvent; @@ -40,6 +42,8 @@ pub enum Event { CallHangup(HangupEvent), /// m.call.invite CallInvite(InviteEvent), + /// m.direct + Direct(DirectEvent), /// m.presence Presence(PresenceEvent), /// m.receipt @@ -64,6 +68,8 @@ pub enum Event { RoomMessage(MessageEvent), /// m.room.name RoomName(NameEvent), + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), /// m.room.power_levels RoomPowerLevels(PowerLevelsEvent), /// m.room.redaction @@ -115,6 +121,8 @@ pub enum RoomEvent { RoomMessage(MessageEvent), /// m.room.name RoomName(NameEvent), + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), /// m.room.power_levels RoomPowerLevels(PowerLevelsEvent), /// m.room.redaction @@ -150,6 +158,8 @@ pub enum StateEvent { RoomMember(MemberEvent), /// m.room.name RoomName(NameEvent), + /// m.room.pinned_events + RoomPinnedEvents(PinnedEventsEvent), /// m.room.power_levels RoomPowerLevels(PowerLevelsEvent), /// m.room.third_party_invite @@ -167,6 +177,7 @@ impl Serialize for Event { 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::Presence(ref event) => event.serialize(serializer), Event::Receipt(ref event) => event.serialize(serializer), Event::RoomAliases(ref event) => event.serialize(serializer), @@ -179,6 +190,7 @@ impl Serialize for Event { Event::RoomMember(ref event) => event.serialize(serializer), Event::RoomMessage(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::RoomThirdPartyInvite(ref event) => event.serialize(serializer), @@ -239,6 +251,14 @@ impl<'de> Deserialize<'de> for Event { Ok(Event::CallInvite(event)) } + EventType::Direct => { + let event = match from_value::(value) { + Ok(event) => event, + Err(error) => return Err(D::Error::custom(error.to_string())), + }; + + Ok(Event::Direct(event)) + } EventType::Presence => { let event = match from_value::(value) { Ok(event) => event, @@ -335,6 +355,14 @@ impl<'de> Deserialize<'de> for Event { Ok(Event::RoomName(event)) } + EventType::RoomPinnedEvents => { + let event = match from_value::(value) { + Ok(event) => event, + Err(error) => return Err(D::Error::custom(error.to_string())), + }; + + Ok(Event::RoomPinnedEvents(event)) + } EventType::RoomPowerLevels => { let event = match from_value::(value) { Ok(event) => event, @@ -429,6 +457,7 @@ impl Serialize for RoomEvent { RoomEvent::RoomMember(ref event) => event.serialize(serializer), RoomEvent::RoomMessage(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::RoomThirdPartyInvite(ref event) => event.serialize(serializer), @@ -566,6 +595,14 @@ impl<'de> Deserialize<'de> for RoomEvent { Ok(RoomEvent::RoomName(event)) } + EventType::RoomPinnedEvents => { + let event = match from_value::(value) { + Ok(event) => event, + Err(error) => return Err(D::Error::custom(error.to_string())), + }; + + Ok(RoomEvent::RoomPinnedEvents(event)) + } EventType::RoomPowerLevels => { let event = match from_value::(value) { Ok(event) => event, @@ -615,7 +652,11 @@ impl<'de> Deserialize<'de> for RoomEvent { Ok(RoomEvent::CustomRoom(event)) } } - EventType::Presence | EventType::Receipt | EventType::Tag | EventType::Typing => { + EventType::Direct | + EventType::Presence | + EventType::Receipt | + EventType::Tag | + EventType::Typing => { return Err(D::Error::custom("not a room event".to_string())); } } @@ -634,6 +675,7 @@ impl Serialize for StateEvent { 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::RoomThirdPartyInvite(ref event) => event.serialize(serializer), StateEvent::RoomTopic(ref event) => event.serialize(serializer), @@ -729,6 +771,14 @@ impl<'de> Deserialize<'de> for StateEvent { Ok(StateEvent::RoomName(event)) } + EventType::RoomPinnedEvents => { + let event = match from_value::(value) { + Ok(event) => event, + Err(error) => return Err(D::Error::custom(error.to_string())), + }; + + Ok(StateEvent::RoomPinnedEvents(event)) + } EventType::RoomPowerLevels => { let event = match from_value::(value) { Ok(event) => event, @@ -761,9 +811,16 @@ impl<'de> Deserialize<'de> for StateEvent { Ok(StateEvent::CustomState(event)) } - EventType::CallAnswer | EventType::CallCandidates | EventType::CallHangup | - EventType::CallInvite | EventType::Presence | EventType::Receipt | - EventType::RoomMessage | EventType::RoomRedaction | EventType::Tag | + EventType::CallAnswer | + EventType::CallCandidates | + EventType::CallHangup | + EventType::CallInvite | + EventType::Direct | + EventType::Presence | + EventType::Receipt | + EventType::RoomMessage | + EventType::RoomRedaction | + EventType::Tag | EventType::Typing => { return Err(D::Error::custom("not a state event".to_string())); } @@ -785,6 +842,7 @@ 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!(PresenceEvent, Presence); impl_from_t_for_event!(ReceiptEvent, Receipt); impl_from_t_for_event!(AliasesEvent, RoomAliases); @@ -797,6 +855,7 @@ 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!(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!(ThirdPartyInviteEvent, RoomThirdPartyInvite); @@ -831,6 +890,7 @@ 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!(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!(ThirdPartyInviteEvent, RoomThirdPartyInvite); @@ -857,6 +917,7 @@ 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!(ThirdPartyInviteEvent, RoomThirdPartyInvite); impl_from_t_for_state_event!(TopicEvent, RoomTopic); diff --git a/src/collections/only.rs b/src/collections/only.rs index 4c433cb2..9f0479df 100644 --- a/src/collections/only.rs +++ b/src/collections/only.rs @@ -6,6 +6,7 @@ use call::answer::AnswerEvent; use call::candidates::CandidatesEvent; use call::hangup::HangupEvent; use call::invite::InviteEvent; +use direct::DirectEvent; use presence::PresenceEvent; use receipt::ReceiptEvent; use room::message::MessageEvent; @@ -22,6 +23,8 @@ pub use super::all::StateEvent; /// A basic event. #[derive(Clone, Debug)] pub enum Event { + /// m.direct + Direct(DirectEvent), /// m.presence Presence(PresenceEvent), /// m.receipt @@ -56,6 +59,7 @@ pub enum RoomEvent { impl Serialize for Event { fn serialize(&self, serializer: S) -> Result where S: Serializer { match *self { + Event::Direct(ref event) => event.serialize(serializer), Event::Presence(ref event) => event.serialize(serializer), Event::Receipt(ref event) => event.serialize(serializer), Event::Tag(ref event) => event.serialize(serializer), @@ -80,6 +84,14 @@ impl<'de> Deserialize<'de> for Event { }; match event_type { + EventType::Direct => { + let event = match from_value::(value) { + Ok(event) => event, + Err(error) => return Err(D::Error::custom(error.to_string())), + }; + + Ok(Event::Direct(event)) + } EventType::Presence => { let event = match from_value::(value) { Ok(event) => event, @@ -124,8 +136,9 @@ impl<'de> Deserialize<'de> for Event { EventType::CallInvite | EventType::RoomAliases | EventType::RoomAvatar | EventType::RoomCanonicalAlias | EventType::RoomCreate | EventType::RoomGuestAccess | EventType::RoomHistoryVisibility | EventType::RoomJoinRules | EventType::RoomMember | - EventType::RoomMessage | EventType::RoomName | EventType::RoomPowerLevels | - EventType::RoomRedaction | EventType::RoomThirdPartyInvite | EventType::RoomTopic => { + EventType::RoomMessage | EventType::RoomName | EventType::RoomPinnedEvents | + EventType::RoomPowerLevels | EventType::RoomRedaction | EventType::RoomThirdPartyInvite | + EventType::RoomTopic => { return Err(D::Error::custom("not exclusively a basic event".to_string())); } } @@ -217,12 +230,24 @@ impl<'de> Deserialize<'de> for RoomEvent { Ok(RoomEvent::CustomRoom(event)) } - EventType::Presence | EventType::Receipt | EventType::RoomAliases | - EventType::RoomAvatar | EventType::RoomCanonicalAlias | EventType::RoomCreate | - EventType::RoomGuestAccess | EventType::RoomHistoryVisibility | - EventType::RoomJoinRules | EventType::RoomMember | EventType::RoomName | - EventType::RoomPowerLevels |EventType::RoomThirdPartyInvite | EventType::RoomTopic | - EventType::Tag | EventType::Typing => { + EventType::Direct | + EventType::Presence | + EventType::Receipt | + EventType::RoomAliases | + EventType::RoomAvatar | + EventType::RoomCanonicalAlias | + EventType::RoomCreate | + EventType::RoomGuestAccess | + EventType::RoomHistoryVisibility | + EventType::RoomJoinRules | + EventType::RoomMember | + EventType::RoomName | + EventType::RoomPinnedEvents | + EventType::RoomPowerLevels | + EventType::RoomThirdPartyInvite | + EventType::RoomTopic | + EventType::Tag | + EventType::Typing => { return Err(D::Error::custom("not exclusively a room event".to_string())); } } @@ -238,6 +263,7 @@ macro_rules! impl_from_t_for_event { }; } +impl_from_t_for_event!(DirectEvent, Direct); impl_from_t_for_event!(PresenceEvent, Presence); impl_from_t_for_event!(ReceiptEvent, Receipt); impl_from_t_for_event!(TagEvent, Tag); diff --git a/src/direct.rs b/src/direct.rs new file mode 100644 index 00000000..b81bfde6 --- /dev/null +++ b/src/direct.rs @@ -0,0 +1,93 @@ +//! Types for the *m.direct* event. + +use std::collections::HashMap; + +use ruma_identifiers::{UserId, RoomId}; + +event! { + /// Informs the client about the rooms that are considered direct by a user. + pub struct DirectEvent(DirectEventContent) {} +} + +/// The payload of a `DirectEvent`. +/// +/// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered +/// *direct* for that particular user. +pub type DirectEventContent = HashMap>; + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use ruma_identifiers::{UserId, RoomId}; + use serde_json::{from_str, to_string}; + + use collections; + use direct::{DirectEvent, DirectEventContent}; + use super::super::EventType; + + #[test] + fn serialization() { + let mut content: DirectEventContent = HashMap::new(); + let alice = UserId::new("ruma.io").unwrap(); + let room = vec![RoomId::new("ruma.io").unwrap()]; + + content.insert(alice.clone(), room.clone()); + + let event = DirectEvent { + content: content, + event_type: EventType::Direct, + }; + + assert_eq!( + to_string(&event).unwrap(), + format!( + r#"{{"content":{{"{}":["{}"]}},"type":"m.direct"}}"#, + alice.to_string(), room[0].to_string() + ) + ); + } + + #[test] + fn deserialization() { + let alice = UserId::new("ruma.io").unwrap(); + let rooms = vec![ + RoomId::new("ruma.io").unwrap(), + RoomId::new("ruma.io").unwrap() + ]; + + let json_data = format!(r#"{{ + "content": {{ "{}": ["{}", "{}"] }}, + "type": "m.direct" + }}"#, alice.to_string(), rooms[0].to_string(), rooms[1].to_string()); + + let event = from_str::(&json_data).unwrap(); + assert_eq!(event.event_type, EventType::Direct); + + let direct_rooms = event.content.get(&alice).unwrap(); + assert!(direct_rooms.contains(&rooms[0])); + assert!(direct_rooms.contains(&rooms[1])); + + match from_str::(&json_data).unwrap() { + collections::all::Event::Direct(event) => { + assert_eq!(event.event_type, EventType::Direct); + + let direct_rooms = event.content.get(&alice).unwrap(); + assert!(direct_rooms.contains(&rooms[0])); + assert!(direct_rooms.contains(&rooms[1])); + }, + _ => assert!(false) + }; + + match from_str::(&json_data).unwrap() { + collections::only::Event::Direct(event) => { + assert_eq!(event.event_type, EventType::Direct); + + let direct_rooms = event.content.get(&alice).unwrap(); + assert!(direct_rooms.contains(&rooms[0])); + assert!(direct_rooms.contains(&rooms[1])); + }, + _ => assert!(false) + }; + } +} diff --git a/src/lib.rs b/src/lib.rs index 3dbb6501..d4f77f71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,7 @@ pub mod collections { pub mod all; pub mod only; } +pub mod direct; pub mod presence; pub mod receipt; pub mod room; @@ -142,6 +143,8 @@ pub enum EventType { CallHangup, /// m.call.invite CallInvite, + /// m.direct + Direct, /// m.presence Presence, /// m.receipt @@ -166,6 +169,8 @@ pub enum EventType { RoomMessage, /// m.room.name RoomName, + /// m.room.pinned_events + RoomPinnedEvents, /// m.room.power_levels RoomPowerLevels, /// m.room.redaction @@ -192,12 +197,6 @@ pub trait Event where Self: Debug + for<'a> Deserialize<'a> + Serialize { /// The type of the event. fn event_type(&self) -> &EventType; - - /// Extra top-level key-value pairs specific to this event type, but that are not under the - /// `content` field. - fn extra_content(&self) -> Option { - None - } } /// An event within the context of a room. @@ -205,14 +204,17 @@ pub trait RoomEvent: Event { /// The unique identifier for the event. fn event_id(&self) -> &EventId; + /// Timestamp in milliseconds on originating homeserver when this event was sent. + fn origin_server_ts(&self) -> u64; + /// The unique identifier for the room associated with this event. fn room_id(&self) -> &RoomId; + /// The unique identifier for the user who sent this event. + fn sender(&self) -> &UserId; + /// Additional key-value pairs not signed by the homeserver. fn unsigned(&self) -> Option<&Value>; - - /// The unique identifier for the user associated with this event. - fn user_id(&self) -> &UserId; } /// An event that describes persistent state about a room. @@ -246,6 +248,7 @@ impl Display for EventType { EventType::CallCandidates => "m.call.candidates", EventType::CallHangup => "m.call.hangup", EventType::CallInvite => "m.call.invite", + EventType::Direct => "m.direct", EventType::Presence => "m.presence", EventType::Receipt => "m.receipt", EventType::RoomAliases => "m.room.aliases", @@ -258,6 +261,7 @@ impl Display for EventType { EventType::RoomMember => "m.room.member", EventType::RoomMessage => "m.room.message", EventType::RoomName => "m.room.name", + EventType::RoomPinnedEvents=> "m.room.pinned_events", EventType::RoomPowerLevels => "m.room.power_levels", EventType::RoomRedaction => "m.room.redaction", EventType::RoomThirdPartyInvite => "m.room.third_party_invite", @@ -278,6 +282,7 @@ impl<'a> From<&'a str> for EventType { "m.call.candidates" => EventType::CallCandidates, "m.call.hangup" => EventType::CallHangup, "m.call.invite" => EventType::CallInvite, + "m.direct" => EventType::Direct, "m.presence" => EventType::Presence, "m.receipt" => EventType::Receipt, "m.room.aliases" => EventType::RoomAliases, @@ -290,6 +295,7 @@ impl<'a> From<&'a str> for EventType { "m.room.member" => EventType::RoomMember, "m.room.message" => EventType::RoomMessage, "m.room.name" => EventType::RoomName, + "m.room.pinned_events" => EventType::RoomPinnedEvents, "m.room.power_levels" => EventType::RoomPowerLevels, "m.room.redaction" => EventType::RoomRedaction, "m.room.third_party_invite" => EventType::RoomThirdPartyInvite, diff --git a/src/macros.rs b/src/macros.rs index 9c7c9f6a..bf182b3a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -90,6 +90,9 @@ macro_rules! room_event { #[serde(rename="type")] pub event_type: $crate::EventType, + /// Timestamp in milliseconds on originating homeserver when this event was sent. + pub origin_server_ts: u64, + /// The unique identifier for the room associated with this event. pub room_id: ::ruma_identifiers::RoomId, @@ -97,9 +100,8 @@ macro_rules! room_event { #[serde(skip_serializing_if="Option::is_none")] pub unsigned: Option<::serde_json::Value>, - /// The unique identifier for the user associated with this event. - #[serde(rename="sender")] - pub user_id: ::ruma_identifiers::UserId, + /// The unique identifier for the user who sent this event. + pub sender: ::ruma_identifiers::UserId, $( $(#[$field_attr])* @@ -120,6 +122,10 @@ macro_rules! impl_room_event { &self.event_id } + fn origin_server_ts(&self) -> u64 { + self.origin_server_ts + } + fn room_id(&self) -> &::ruma_identifiers::RoomId { &self.room_id } @@ -128,8 +134,8 @@ macro_rules! impl_room_event { self.unsigned.as_ref() } - fn user_id(&self) -> &::ruma_identifiers::UserId { - &self.user_id + fn sender(&self) -> &::ruma_identifiers::UserId { + &self.sender } } } @@ -158,6 +164,9 @@ macro_rules! state_event { #[serde(rename="type")] pub event_type: $crate::EventType, + /// Timestamp in milliseconds on originating homeserver when this event was sent. + pub origin_server_ts: u64, + /// The previous content for this state key, if any. #[serde(skip_serializing_if="Option::is_none")] pub prev_content: Option<$content_type>, @@ -173,8 +182,7 @@ macro_rules! state_event { pub unsigned: Option<::serde_json::Value>, /// The unique identifier for the user associated with this event. - #[serde(rename="sender")] - pub user_id: ::ruma_identifiers::UserId, + pub sender: ::ruma_identifiers::UserId, $( $(#[$field_attr])* diff --git a/src/presence.rs b/src/presence.rs index 5ed4001f..ac905c8e 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -25,7 +25,7 @@ pub struct PresenceEventContent { #[serde(skip_serializing_if="Option::is_none")] pub displayname: Option, - /// The last time since this used performed some action, in milliseconds. + /// The last time since this user performed some action, in milliseconds. #[serde(skip_serializing_if="Option::is_none")] pub last_active_ago: Option, diff --git a/src/room/create.rs b/src/room/create.rs index c9fc5fe1..0e073241 100644 --- a/src/room/create.rs +++ b/src/room/create.rs @@ -14,5 +14,6 @@ pub struct CreateEventContent { /// The `user_id` of the room creator. This is set by the homeserver. pub creator: UserId, /// Whether or not this room's data should be transferred to other homeservers. + #[serde(rename="m.federate")] pub federate: Option, } diff --git a/src/room/member.rs b/src/room/member.rs index ff6c0219..95aec702 100644 --- a/src/room/member.rs +++ b/src/room/member.rs @@ -38,6 +38,11 @@ pub struct MemberEventContent { #[serde(skip_serializing_if="Option::is_none")] pub displayname: Option, + /// Flag indicating if the room containing this event was created + /// with the intention of being a direct chat. + #[serde(skip_serializing_if="Option::is_none")] + pub is_direct: Option, + /// The membership state of this user. pub membership: MembershipState, diff --git a/src/room/message.rs b/src/room/message.rs index 487ff2f3..b6ff3d07 100644 --- a/src/room/message.rs +++ b/src/room/message.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::de::Error; use serde_json::{Value, from_value}; -use super::ImageInfo; +use super::{ImageInfo, ThumbnailInfo}; room_event! { /// A message sent to a room. @@ -118,17 +118,13 @@ pub struct FileMessageEventContent { /// A human-readable description of the file. This is recommended to be the filename of the /// original upload. pub body: String, + /// The original filename of the uploaded file. + pub filename: String, /// Metadata about the file referred to in `url`. #[serde(skip_serializing_if="Option::is_none")] pub info: Option, /// The message type. Always *m.file*. pub msgtype: MessageType, - /// Metadata about the image referred to in `thumbnail_url`. - #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_info: Option, - /// The URL to the thumbnail of the file. - #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_url: Option, /// The URL to the file. pub url: String, } @@ -140,6 +136,12 @@ pub struct FileInfo { pub mimetype: String, /// The size of the file in bytes. pub size: u64, + /// Metadata about the image referred to in `thumbnail_url`. + #[serde(skip_serializing_if="Option::is_none")] + pub thumbnail_info: Option, + /// The URL to the thumbnail of the file. + #[serde(skip_serializing_if="Option::is_none")] + pub thumbnail_url: Option, } /// The payload of an image message. @@ -153,12 +155,6 @@ pub struct ImageMessageEventContent { pub info: Option, /// The message type. Always *m.image*. pub msgtype: MessageType, - /// Metadata about the image referred to in `thumbnail_url`. - #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_info: Option, - /// The URL to the thumbnail of the image. - #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_url: Option, /// The URL to the image. pub url: String, } @@ -173,9 +169,17 @@ pub struct LocationMessageEventContent { pub geo_uri: String, /// The message type. Always *m.location*. pub msgtype: MessageType, + /// Info about the location being represented. + #[serde(skip_serializing_if="Option::is_none")] + pub info: Option, +} + +/// Thumbnail info associated with a location. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct LocationInfo { /// Metadata about the image referred to in `thumbnail_url`. #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_info: Option, + pub thumbnail_info: Option, /// The URL to a thumbnail of the location being represented. #[serde(skip_serializing_if="Option::is_none")] pub thumbnail_url: Option, @@ -232,7 +236,7 @@ pub struct VideoInfo { pub size: Option, /// Metadata about an image. #[serde(skip_serializing_if="Option::is_none")] - pub thumbnail_info: Option, + pub thumbnail_info: Option, /// The URL to a thumbnail of the video clip. #[serde(skip_serializing_if="Option::is_none")] pub thumbnail_url: Option, diff --git a/src/room/mod.rs b/src/room/mod.rs index 232f7556..33283000 100644 --- a/src/room/mod.rs +++ b/src/room/mod.rs @@ -12,6 +12,7 @@ pub mod join_rules; pub mod member; pub mod message; pub mod name; +pub mod pinned_events; pub mod power_levels; pub mod redaction; pub mod third_party_invite; @@ -21,13 +22,34 @@ pub mod topic; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct ImageInfo { /// The height of the image in pixels. - #[serde(rename = "h")] + #[serde(rename="h")] pub height: u64, /// The MIME type of the image, e.g. "image/png." pub mimetype: String, /// The file size of the image in bytes. pub size: u64, + /// Metadata about the image referred to in `thumbnail_url`. + #[serde(skip_serializing_if="Option::is_none")] + pub thumbnail_info: Option, + /// The URL to the thumbnail of the image. + #[serde(skip_serializing_if="Option::is_none")] + pub thumbnail_url: Option, /// The width of the image in pixels. - #[serde(rename = "w")] + #[serde(rename="w")] + pub width: u64, +} + +/// Metadata about a thumbnail. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct ThumbnailInfo { + /// The height of the thumbnail in pixels. + #[serde(rename="h")] + pub height: u64, + /// The MIME type of the thumbnail, e.g. "image/png." + pub mimetype: String, + /// The file size of the thumbnail in bytes. + pub size: u64, + /// The width of the thumbnail in pixels. + #[serde(rename="w")] pub width: u64, } diff --git a/src/room/pinned_events.rs b/src/room/pinned_events.rs new file mode 100644 index 00000000..a96eb40d --- /dev/null +++ b/src/room/pinned_events.rs @@ -0,0 +1,63 @@ +//! Types for the *m.room.pinned_events* event. + +use ruma_identifiers::EventId; + +state_event! { + /// Used to "pin" particular events in a room for other participants to review later. + pub struct PinnedEventsEvent(PinnedEventsContent) {} +} + +/// The payload of a `NameEvent`. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PinnedEventsContent { + /// An ordered list of event IDs to pin. + pub pinned: Vec, +} + +#[cfg(test)] +mod tests { + use ruma_identifiers::{EventId, RoomId, UserId}; + use serde_json::{from_str, to_string}; + + use Event; + use EventType; + use RoomEvent; + use StateEvent; + use room::pinned_events::{PinnedEventsEvent, PinnedEventsContent}; + + #[test] + fn serialization_deserialization() { + let mut content: PinnedEventsContent = PinnedEventsContent { + pinned: Vec::new() + }; + + content.pinned.push(EventId::new("example.com").unwrap()); + content.pinned.push(EventId::new("example.com").unwrap()); + + let event = PinnedEventsEvent { + content: content.clone(), + event_id: EventId::new("example.com").unwrap(), + event_type: EventType::RoomPinnedEvents, + origin_server_ts: 1432804485886, + prev_content: None, + room_id: RoomId::new("example.com").unwrap(), + sender: UserId::new("example.com").unwrap(), + state_key: "".to_string(), + unsigned: None, + }; + + let serialized_event = to_string(&event).unwrap(); + let parsed_event: PinnedEventsEvent = from_str(&serialized_event).unwrap(); + + assert_eq!(parsed_event.event_id(), event.event_id()); + assert_eq!(parsed_event.room_id(), event.room_id()); + assert_eq!(parsed_event.sender(), event.sender()); + assert_eq!(parsed_event.unsigned(), event.unsigned()); + assert_eq!(parsed_event.state_key(), event.state_key()); + assert_eq!(parsed_event.origin_server_ts(), event.origin_server_ts()); + + assert_eq!(parsed_event.content().pinned, event.content.pinned); + assert_eq!(parsed_event.content().pinned[0], content.pinned[0]); + assert_eq!(parsed_event.content().pinned[1], content.pinned[1]); + } +} diff --git a/src/stripped.rs b/src/stripped.rs index 28d50500..60181cf6 100644 --- a/src/stripped.rs +++ b/src/stripped.rs @@ -294,7 +294,14 @@ mod tests { "h": 128, "w": 128, "mimetype": "image/jpeg", - "size": 1024 + "size": 1024, + "thumbnail_info": { + "h": 16, + "w": 16, + "mimetype": "image/jpeg", + "size": 32 + }, + "thumbnail_url": "https://domain.com/image-thumbnail.jpg" }, "thumbnail_info": { "h": 16, diff --git a/src/typing.rs b/src/typing.rs index d5bdd22a..6ba07abd 100644 --- a/src/typing.rs +++ b/src/typing.rs @@ -1,6 +1,6 @@ //! Types for the *m.typing* event. -use ruma_identifiers::{EventId, RoomId}; +use ruma_identifiers::{RoomId, UserId}; event! { /// Informs the client of the list of users currently typing. @@ -14,5 +14,5 @@ event! { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TypingEventContent { /// The list of user IDs typing in this room, if any. - pub user_ids: Vec, + pub user_ids: Vec, }