diff --git a/src/macros.rs b/src/macros.rs index ccab4112..b59278a1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,3 +23,77 @@ macro_rules! impl_enum { } } } + +macro_rules! impl_event { + ($name:ident, $content_name:ident, $event_type:path) => { + impl Event for $name { + /// The type of this event's `content` field. + type Content = $content_name; + + /// The event's content. + fn content(&self) -> &Self::Content { + &self.content + } + + /// The type of the event. + fn event_type(&self) -> EventType { + $event_type + } + } + }; +} + +macro_rules! impl_room_event { + ($name:ident, $content_name:ident, $event_type:path) => { + impl_event!($name, $content_name, $event_type); + + impl RoomEvent for $name { + /// The unique identifier for the event. + fn event_id(&self) -> &EventId { + &self.event_id + } + + /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was + /// sent. + fn origin_server_ts(&self) -> UInt { + self.origin_server_ts + } + + /// The unique identifier for the room associated with this event. + /// + /// This can be `None` if the event came from a context where there is + /// no ambiguity which room it belongs to, like a `/sync` response for example. + fn room_id(&self) -> Option<&RoomId> { + self.room_id.as_ref() + } + + /// The unique identifier for the user who sent this event. + fn sender(&self) -> &UserId { + &self.sender + } + + /// Additional key-value pairs not signed by the homeserver. + fn unsigned(&self) -> Option<&Value> { + self.unsigned.as_ref() + } + } + }; +} + +macro_rules! impl_state_event { + ($name:ident, $content_name:ident, $event_type:path) => { + impl_room_event!($name, $content_name, $event_type); + + impl StateEvent for $name { + /// The previous content for this state key, if any. + fn prev_content(&self) -> Option<&Self::Content> { + self.prev_content.as_ref() + } + + /// A key that determines which piece of room state the event represents. + fn state_key(&self) -> &str { + &self.state_key + } + } + }; +} diff --git a/src/room/name.rs b/src/room/name.rs index 1d73a736..22aa32ac 100644 --- a/src/room/name.rs +++ b/src/room/name.rs @@ -40,7 +40,7 @@ pub struct NameEvent { pub state_key: String, } -/// The payload of a `NameEvent`. +/// The payload for `NameEvent`. #[derive(Clone, Debug, PartialEq, Serialize)] pub struct NameEventContent { /// The name of the room. This MUST NOT exceed 255 bytes. @@ -91,72 +91,47 @@ impl Serialize for NameEvent { where S: Serializer, { - let mut state = serializer.serialize_struct("NameEvent", 2)?; + let mut len = 6; + + if self.prev_content.is_some() { + len += 1; + } + + if self.room_id.is_some() { + len += 1; + } + + if self.unsigned.is_some() { + len += 1; + } + + let mut state = serializer.serialize_struct("NameEvent", len)?; state.serialize_field("content", &self.content)?; + state.serialize_field("event_id", &self.event_id)?; + state.serialize_field("origin_server_ts", &self.origin_server_ts)?; + + if self.prev_content.is_some() { + state.serialize_field("prev_content", &self.prev_content)?; + } + + if self.room_id.is_some() { + state.serialize_field("room_id", &self.room_id)?; + } + + if self.unsigned.is_some() { + state.serialize_field("unsigned", &self.unsigned)?; + } + + state.serialize_field("sender", &self.sender)?; + state.serialize_field("state_key", &self.state_key)?; state.serialize_field("type", &self.event_type())?; state.end() } } -impl Event for NameEvent { - /// The type of this event's `content` field. - type Content = NameEventContent; - - /// The event's content. - fn content(&self) -> &Self::Content { - &self.content - } - - /// The type of the event. - fn event_type(&self) -> EventType { - EventType::RoomName - } -} - -impl RoomEvent for NameEvent { - /// The unique identifier for the event. - fn event_id(&self) -> &EventId { - &self.event_id - } - - /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was - /// sent. - fn origin_server_ts(&self) -> UInt { - self.origin_server_ts - } - - /// The unique identifier for the room associated with this event. - /// - /// This can be `None` if the event came from a context where there is - /// no ambiguity which room it belongs to, like a `/sync` response for example. - fn room_id(&self) -> Option<&RoomId> { - self.room_id.as_ref() - } - - /// The unique identifier for the user who sent this event. - fn sender(&self) -> &UserId { - &self.sender - } - - /// Additional key-value pairs not signed by the homeserver. - fn unsigned(&self) -> Option<&Value> { - self.unsigned.as_ref() - } -} - -impl StateEvent for NameEvent { - /// The previous content for this state key, if any. - fn prev_content(&self) -> Option<&Self::Content> { - self.prev_content.as_ref() - } - - /// A key that determines which piece of room state the event represents. - fn state_key(&self) -> &str { - &self.state_key - } -} +impl_state_event!(NameEvent, NameEventContent, EventType::RoomName); impl NameEventContent { /// Create a new `NameEventContent` with the given name. diff --git a/src/room/power_levels.rs b/src/room/power_levels.rs index 7405fd5a..e205bc08 100644 --- a/src/room/power_levels.rs +++ b/src/room/power_levels.rs @@ -1,20 +1,48 @@ //! Types for the *m.room.power_levels* event. -use std::collections::HashMap; +use std::{collections::HashMap, convert::TryFrom, str::FromStr}; -use js_int::{Int, UInt}; -use ruma_identifiers::UserId; -use serde::{Deserialize, Serialize}; +use js_int::UInt; +use ruma_identifiers::{EventId, RoomId, UserId}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; +use serde_json::Value; -use crate::EventType; +use crate::{ + Event, EventType, InvalidEvent, InvalidInput, RoomEvent, StateEvent, +}; -state_event! { - /// Defines the power levels (privileges) of users in the room. - pub struct PowerLevelsEvent(PowerLevelsEventContent) {} + +/// Defines the power levels (privileges) of users in the room. +#[derive(Clone, Debug, PartialEq)] +pub struct PowerLevelsEvent { + /// The event's content. + pub content: PowerLevelsEventContent, + + /// The unique identifier for the event. + pub event_id: EventId, + + /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this + /// event was sent. + pub origin_server_ts: UInt, + + /// 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: Option, + + /// Additional key-value pairs not signed by the homeserver. + pub unsigned: Option, + + /// The unique identifier for the user who sent this event. + pub sender: UserId, + + /// A key that determines which piece of room state the event represents. + pub state_key: String, } -/// The payload of a `PowerLevelsEvent`. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +/// The payload for `PowerLevelsEvent`. +#[derive(Clone, Debug, PartialEq, Serialize)] pub struct PowerLevelsEventContent { /// The level required to ban a user. #[serde(default = "default_power_level")] @@ -60,6 +88,186 @@ pub struct PowerLevelsEventContent { pub notifications: NotificationPowerLevels, } +impl FromStr for PowerLevelsEvent { + type Err = crate::InvalidEvent; + + /// Attempt to create `Self` from parsing a string of JSON data. + fn from_str(json: &str) -> Result { + let raw = serde_json::from_str::(json)?; + + Ok(Self { + 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, + }, + 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, + }), + room_id: raw.room_id, + unsigned: raw.unsigned, + sender: raw.sender, + state_key: raw.state_key, + }) + } +} + +impl<'a> TryFrom<&'a str> for PowerLevelsEvent { + type Error = InvalidEvent; + + /// Attempt to create `Self` from parsing a string of JSON data. + fn try_from(json: &'a str) -> Result { + FromStr::from_str(json) + } +} + +impl Serialize for NameEvent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut len = 6; + + if self.prev_content.is_some() { + len += 1; + } + + if self.room_id.is_some() { + len += 1; + } + + if self.unsigned.is_some() { + len += 1; + } + + let mut state = serializer.serialize_struct("PowerLevelsEvent", len)?; + + state.serialize_field("content", &self.content)?; + state.serialize_field("event_id", &self.event_id)?; + state.serialize_field("origin_server_ts", &self.origin_server_ts)?; + + if self.prev_content.is_some() { + state.serialize_field("prev_content", &self.prev_content)?; + } + + if self.room_id.is_some() { + state.serialize_field("room_id", &self.room_id)?; + } + + if self.unsigned.is_some() { + state.serialize_field("unsigned", &self.unsigned)?; + } + + state.serialize_field("sender", &self.sender)?; + state.serialize_field("state_key", &self.state_key)?; + state.serialize_field("type", &self.event_type())?; + + state.end() + } +} + +impl_state_event!(PowerLevelsEvent, PowerLevelsEventContent, EventType::RoomPowerLevels); + +mod raw { + use super::*; + + /// Defines the power levels (privileges) of users in the room. + #[derive(Clone, Debug, Deserialize, PartialEq)] + pub struct PowerLevelsEvent { + /// The event's content. + pub content: PowerLevelsEventContent, + + /// The unique identifier for the event. + pub event_id: EventId, + + /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this + /// event was sent. + pub origin_server_ts: UInt, + + /// 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: Option, + + /// Additional key-value pairs not signed by the homeserver. + pub unsigned: Option, + + /// The unique identifier for the user who sent this event. + pub sender: UserId, + + /// A key that determines which piece of room state the event represents. + pub state_key: String, + } + + /// The payload for `PowerLevelsEvent`. + #[derive(Clone, Debug, Deserialize, PartialEq)] + pub struct PowerLevelsEventContent { + /// The level required to ban a user. + #[serde(default = "default_power_level")] + pub ban: Int, + + /// The level required to send specific event types. + /// + /// This is a mapping from event type to power level required. + pub events: HashMap, + + /// The default level required to send message events. + #[serde(default)] + pub events_default: Int, + + /// The level required to invite a user. + #[serde(default = "default_power_level")] + pub invite: Int, + + /// The level required to kick a user. + #[serde(default = "default_power_level")] + pub kick: Int, + + /// The level required to redact an event. + #[serde(default = "default_power_level")] + pub redact: Int, + + /// The default level required to send state events. + #[serde(default = "default_power_level")] + pub state_default: Int, + + /// The power levels for specific users. + /// + /// This is a mapping from `user_id` to power level for that user. + pub users: HashMap, + + /// The default power level for every user in the room. + #[serde(default)] + pub users_default: Int, + + /// The power level requirements for specific notification types. + /// + /// This is a mapping from `key` to power level for that notifications key. + pub notifications: NotificationPowerLevels, + } +} + /// The power level requirements for specific notification types. #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct NotificationPowerLevels {