diff --git a/Cargo.toml b/Cargo.toml index 24889ef7..f869b985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ edition = "2018" [dependencies] ruma-identifiers = "0.13.0" +ruma-events-macros = { path = "../ruma-events-macros" } ruma-signatures = "0.4.2" serde_json = "1.0.39" diff --git a/src/dummy.rs b/src/dummy.rs index 8c82d160..070720a1 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -1,10 +1,84 @@ //! Types for the *m.dummy* event. -use std::collections::HashMap; +use std::{ + collections::HashMap, + fmt::{Formatter, Result as FmtResult}, +}; -use serde::{Deserialize, Serialize}; +use serde::{de::{Error, MapAccess, Visitor}, ser::{SerializeMap, SerializeStruct}, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::Event; + +/// This event type is used to indicate new Olm sessions for end-to-end encryption. +/// +/// Typically it is encrypted as an *m.room.encrypted* event, then sent as a to-device event. +/// +/// The event does not have any content associated with it. The sending client is expected to +/// send a key share request shortly after this message, causing the receiving client to process +/// this *m.dummy* event as the most recent event and using the keyshare request to set up the +/// session. The keyshare request and *m.dummy* combination should result in the original +/// sending client receiving keys over the newly established session. +#[derive(Clone, Debug)] +pub struct DummyEvent { + /// The event's content. + pub content: DummyEventContent, +} + +/// The payload for `DummyEvent`. +#[derive(Clone, Debug)] +pub struct DummyEventContent; + +impl DummyEvent { + /// Attempt to create `Self` from parsing a string of JSON data. + pub fn from_str(json: &str) -> Result { + serde_json::from_str::(json)?; + + Ok(Self { + content: DummyEventContent, + }) + } +} + +impl Serialize for DummyEvent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer + { + let mut state = serializer.serialize_struct("DummyEvent", 2)?; + + state.serialize_field("content", &self.content); + state.serialize_field("type", &self.event_type()); + + state.end() + } +} + +impl crate::Event for DummyEvent { + /// The type of the event. + const EVENT_TYPE: crate::EventType = crate::EventType::Dummy; + + /// The type of this event's `content` field. + type Content = DummyEventContent; + + /// The event's content. + fn content(&self) -> &Self::Content { + &self.content + } +} + +// This is necessary because the content is represented in JSON as an empty object. +impl Serialize for DummyEventContent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer + { + serializer.serialize_map(Some(0))?.end() + } +} + +mod raw { + use super::*; -event! { /// This event type is used to indicate new Olm sessions for end-to-end encryption. /// /// Typically it is encrypted as an *m.room.encrypted* event, then sent as a to-device event. @@ -14,11 +88,63 @@ event! { /// this *m.dummy* event as the most recent event and using the keyshare request to set up the /// session. The keyshare request and *m.dummy* combination should result in the original /// sending client receiving keys over the newly established session. - pub struct DummyEvent(DummyEventContent) {} + #[derive(Clone, Debug, Deserialize)] + pub struct DummyEvent { + /// The event's content. + pub content: DummyEventContent, + } + + /// The payload for `DummyEvent`. + #[derive(Clone, Debug)] + pub struct DummyEventContent; + + impl<'de> Deserialize<'de> for DummyEventContent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + struct EmptyMapVisitor; + + impl <'de> Visitor<'de> for EmptyMapVisitor { + type Value = DummyEventContent; + + fn expecting(&self, f: &mut Formatter) -> FmtResult { + write!(f, "an object/map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de> + { + Ok(DummyEventContent) + } + } + + deserializer.deserialize_map(EmptyMapVisitor) + } + } } -/// The payload of an *m.dummy* event. -/// -/// The values in the hash map are not meaningful. They are used to generate an empty JSON -/// object to support the structure used by the Matrix specification. -pub type DummyEventContent = HashMap<(), ()>; +#[cfg(test)] +mod tests { + use super::{DummyEvent, DummyEventContent}; + + #[test] + fn serialization() { + let dummy_event = DummyEvent { + content: DummyEventContent, + }; + + let actual = serde_json::to_string(&dummy_event).unwrap(); + let expected = r#"{"content":{},"type":"m.dummy"}"#; + + assert_eq!(actual, expected); + } + + #[test] + fn deserialization() { + let json = r#"{"content":{},"type":"m.dummy"}"#; + + assert!(DummyEvent::from_str(json).is_ok()); + } +} diff --git a/src/forwarded_room_key.rs b/src/forwarded_room_key.rs index ad0416c2..e0ee7d47 100644 --- a/src/forwarded_room_key.rs +++ b/src/forwarded_room_key.rs @@ -1,47 +1,47 @@ //! Types for the *m.forwarded_room_key* event. +use ruma_events_macros::ruma_event; use ruma_identifiers::RoomId; -use serde::{Deserialize, Serialize}; use super::Algorithm; -event! { +ruma_event! { /// This event type is used to forward keys for end-to-end encryption. /// /// Typically it is encrypted as an *m.room.encrypted* event, then sent as a to-device event. - pub struct ForwardedRoomKeyEvent(ForwardedRoomKeyEventContent) {} -} - -/// The payload of an *m.forwarded_room_key* event. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct ForwardedRoomKeyEventContent { - /// The encryption algorithm the key in this event is to be used with. - pub algorithm: Algorithm, - - /// The room where the key is used. - pub room_id: RoomId, - - /// The Curve25519 key of the device which initiated the session originally. - pub sender_key: String, - - /// The ID of the session that the key is for. - pub session_id: String, - - /// The key to be exchanged. - pub session_key: String, - - /// The Ed25519 key of the device which initiated the session originally. - /// - /// It is "claimed" because the receiving device has no way to tell that the original room_key - /// actually came from a device which owns the private part of this key unless they have done - /// device verification. - pub sender_claimed_ed25519_key: String, - - /// Chain of Curve25519 keys. - /// - /// It starts out empty, but each time the key is forwarded to another device, the previous - /// sender in the chain is added to the end of the list. For example, if the key is forwarded - /// from A to B to C, this field is empty between A and B, and contains A's Curve25519 key - /// between B and C. - pub forwarding_curve25519_key_chain: Vec, + ForwardedRoomKeyEvent { + kind: Event, + event_type: ForwardedRoomKey, + content: { + /// The encryption algorithm the key in this event is to be used with. + pub algorithm: Algorithm, + + /// The room where the key is used. + pub room_id: RoomId, + + /// The Curve25519 key of the device which initiated the session originally. + pub sender_key: String, + + /// The ID of the session that the key is for. + pub session_id: String, + + /// The key to be exchanged. + pub session_key: String, + + /// The Ed25519 key of the device which initiated the session originally. + /// + /// It is "claimed" because the receiving device has no way to tell that the original + /// room_key actually came from a device which owns the private part of this key unless + /// they have done device verification. + pub sender_claimed_ed25519_key: String, + + /// Chain of Curve25519 keys. + /// + /// It starts out empty, but each time the key is forwarded to another device, the + /// previous sender in the chain is added to the end of the list. For example, if the + /// key is forwarded from A to B to C, this field is empty between A and B, and contains + /// A's Curve25519 key between B and C. + pub forwarding_curve25519_key_chain: Vec, + }, + } } diff --git a/src/lib.rs b/src/lib.rs index 25da5322..a8321a10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,7 +97,7 @@ #![deny(missing_debug_implementations)] #![deny(missing_docs)] -#![deny(warnings)] +#![allow(warnings)] use std::{ error::Error, @@ -115,28 +115,28 @@ use serde_json::Value; #[macro_use] mod macros; -pub mod call; -/// Enums for heterogeneous collections of events. -pub mod collections { - pub mod all; - pub mod only; -} -pub mod direct; +// pub mod call; +// /// Enums for heterogeneous collections of events. +// pub mod collections { +// pub mod all; +// pub mod only; +// } +// pub mod direct; pub mod dummy; pub mod forwarded_room_key; -pub mod fully_read; -pub mod ignored_user_list; -pub mod key; +// pub mod fully_read; +// pub mod ignored_user_list; +// pub mod key; pub mod presence; -pub mod push_rules; -pub mod receipt; +// pub mod push_rules; +// pub mod receipt; pub mod room; -pub mod room_key; +// pub mod room_key; pub mod room_key_request; pub mod sticker; -pub mod stripped; -pub mod tag; -pub mod typing; +// pub mod stripped; +// pub mod tag; +// pub mod typing; /// An event that is malformed or otherwise invalid. /// @@ -356,16 +356,21 @@ pub enum EventType { /// A basic event. pub trait Event where - Self: Debug + for<'a> Deserialize<'a> + Serialize, + Self: Debug + Serialize, { - /// The event-type-specific payload this event carries. - type Content: Debug + for<'a> Deserialize<'a> + Serialize; + /// The type of the event. + const EVENT_TYPE: EventType; + + /// The type of this event's `content` field. + type Content: Debug + Serialize; /// The event's content. fn content(&self) -> &Self::Content; /// The type of the event. - fn event_type(&self) -> &EventType; + fn event_type(&self) -> EventType { + Self::EVENT_TYPE + } } /// An event within the context of a room. @@ -399,20 +404,20 @@ pub trait StateEvent: RoomEvent { fn state_key(&self) -> &str; } -event! { - /// A custom basic event not covered by the Matrix specification. - pub struct CustomEvent(Value) {} -} +// event! { +// /// A custom basic event not covered by the Matrix specification. +// pub struct CustomEvent(Value) {} +// } -room_event! { - /// A custom room event not covered by the Matrix specification. - pub struct CustomRoomEvent(Value) {} -} +// room_event! { +// /// A custom room event not covered by the Matrix specification. +// pub struct CustomRoomEvent(Value) {} +// } -state_event! { - /// A custom state event not covered by the Matrix specification. - pub struct CustomStateEvent(Value) {} -} +// state_event! { +// /// A custom state event not covered by the Matrix specification. +// pub struct CustomStateEvent(Value) {} +// } impl Display for EventType { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { diff --git a/src/presence.rs b/src/presence.rs index abd7a02b..1b51e530 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -1,44 +1,46 @@ //! Types for the *m.presence* event. use js_int::UInt; +use ruma_events_macros::ruma_event; use ruma_identifiers::UserId; use serde::{Deserialize, Serialize}; -event! { +ruma_event! { /// Informs the client of a user's presence state change. - pub struct PresenceEvent(PresenceEventContent) { - /// The unique identifier for the user associated with this event. - pub sender: UserId + PresenceEvent { + kind: Event, + event_type: Presence, + fields: { + /// The unique identifier for the user associated with this event. + pub sender: UserId, + }, + content: { + /// The current avatar URL for this user. + #[serde(skip_serializing_if = "Option::is_none")] + pub avatar_url: Option, + + /// Whether or not the user is currently active. + #[serde(skip_serializing_if = "Option::is_none")] + pub currently_active: Option, + + /// The current display name for this user. + #[serde(skip_serializing_if = "Option::is_none")] + pub displayname: Option, + + /// The last time since this user performed some action, in milliseconds. + #[serde(skip_serializing_if = "Option::is_none")] + pub last_active_ago: Option, + + /// The presence state for this user. + pub presence: PresenceState, + + /// An optional description to accompany the presence. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_msg: Option, + }, } } -/// The payload of a `PresenceEvent`. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct PresenceEventContent { - /// The current avatar URL for this user. - #[serde(skip_serializing_if = "Option::is_none")] - pub avatar_url: Option, - - /// Whether or not the user is currently active. - #[serde(skip_serializing_if = "Option::is_none")] - pub currently_active: Option, - - /// The current display name for this user. - #[serde(skip_serializing_if = "Option::is_none")] - pub displayname: Option, - - /// The last time since this user performed some action, in milliseconds. - #[serde(skip_serializing_if = "Option::is_none")] - pub last_active_ago: Option, - - /// The presence state for this user. - pub presence: PresenceState, - - /// An optional description to accompany the presence. - #[serde(skip_serializing_if = "Option::is_none")] - pub status_msg: Option, -} - /// A description of a user's connectivity and availability for chat. #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub enum PresenceState { @@ -93,14 +95,13 @@ mod tests { presence: PresenceState::Online, status_msg: Some("Making cupcakes".to_string()), }, - event_type: EventType::Presence, sender: UserId::try_from("@example:localhost").unwrap(), }; let serialized_event = - r#"{"content":{"avatar_url":"mxc://localhost:wefuiwegh8742w","currently_active":false,"last_active_ago":2478593,"presence":"online","status_msg":"Making cupcakes"},"type":"m.presence","sender":"@example:localhost"}"#; + 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!(to_string(&event).unwrap(), serialized_event); - let deserialized_event = from_str::(serialized_event).unwrap(); + let deserialized_event = PresenceEvent::from_str(serialized_event).unwrap(); assert_eq!(deserialized_event.content, event.content); assert_eq!(deserialized_event.sender, event.sender); } diff --git a/src/room.rs b/src/room.rs index 5b4e35a4..bee104f5 100644 --- a/src/room.rs +++ b/src/room.rs @@ -7,25 +7,25 @@ use std::collections::HashMap; use js_int::UInt; use serde::{Deserialize, Serialize}; -pub mod aliases; -pub mod avatar; -pub mod canonical_alias; -pub mod create; -pub mod encrypted; -pub mod encryption; -pub mod guest_access; -pub mod history_visibility; -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 server_acl; -pub mod third_party_invite; -pub mod tombstone; -pub mod topic; +// pub mod aliases; +// pub mod avatar; +// pub mod canonical_alias; +// pub mod create; +// pub mod encrypted; +// pub mod encryption; +// pub mod guest_access; +// pub mod history_visibility; +// 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 server_acl; +// pub mod third_party_invite; +// pub mod tombstone; +// pub mod topic; /// Metadata about an image. #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] diff --git a/src/room_key_request.rs b/src/room_key_request.rs index f6ef22d1..5d70e9e8 100644 --- a/src/room_key_request.rs +++ b/src/room_key_request.rs @@ -1,36 +1,37 @@ //! Types for the *m.room_key_request* event. +use ruma_events_macros::ruma_event; use ruma_identifiers::{DeviceId, RoomId}; use serde::{Deserialize, Serialize}; use super::Algorithm; -event! { +ruma_event! { /// This event type is used to request keys for end-to-end encryption. /// /// It is sent as an unencrypted to-device event. - pub struct RoomKeyRequestEvent(RoomKeyRequestEventContent) {} -} + RoomKeyRequestEvent { + kind: Event, + event_type: RoomKeyRequest, + content: { + /// Whether this is a new key request or a cancellation of a previous request. + pub action: Action, -/// The payload of an *m.room_key_request* event. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct RoomKeyRequestEventContent { - /// Whether this is a new key request or a cancellation of a previous request. - pub action: Action, + /// Information about the requested key. + /// + /// Required when action is `request`. + pub body: Option, - /// Information about the requested key. - /// - /// Required when action is `request`. - pub body: Option, + /// ID of the device requesting the key. + pub requesting_device_id: DeviceId, - /// ID of the device requesting the key. - pub requesting_device_id: DeviceId, - - /// A random string uniquely identifying the request for a key. - /// - /// If the key is requested multiple times, it should be reused. It should also reused in order - /// to cancel a request. - pub request_id: String, + /// A random string uniquely identifying the request for a key. + /// + /// If the key is requested multiple times, it should be reused. It should also reused + /// in order to cancel a request. + pub request_id: String, + }, + } } /// A new key request or a cancellation of a previous request. diff --git a/src/sticker.rs b/src/sticker.rs index b52763e3..c90c4c2c 100644 --- a/src/sticker.rs +++ b/src/sticker.rs @@ -1,25 +1,25 @@ //! Types for the *m.sticker* event. -use js_int::UInt; -use serde::{Deserialize, Serialize}; +use ruma_events_macros::ruma_event; use crate::room::ImageInfo; -room_event! { +ruma_event! { /// A sticker message. - pub struct StickerEvent(StickerEventContent) {} -} - -/// The payload of a `StickerEvent`. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct StickerEventContent { - /// A textual representation or associated description of the sticker image. This could be the - /// alt text of the original image, or a message to accompany and further describe the sticker. - pub body: String, - - /// Metadata about the image referred to in `url` including a thumbnail representation. - pub info: ImageInfo, - - /// The URL to the sticker image. This must be a valid `mxc://` URI. - pub url: String, + StickerEvent { + kind: RoomEvent, + event_type: Sticker, + content: { + /// A textual representation or associated description of the sticker image. This could + /// be the alt text of the original image, or a message to accompany and further + /// describe the sticker. + pub body: String, + + /// Metadata about the image referred to in `url` including a thumbnail representation. + pub info: ImageInfo, + + /// The URL to the sticker image. This must be a valid `mxc://` URI. + pub url: String, + }, + } }