//! "Stripped-down" versions of the core state events. //! //! Each "stripped" event includes only the `content`, `type`, and `state_key` fields of its full //! version. These stripped types are useful for APIs where the user is providing the content of a //! state event to be created, when the other fields can be inferred from a larger context, or where //! the other fields are otherwise inapplicable. use ruma_identifiers::UserId; use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{from_value, Value}; use crate::{ room::{ aliases::AliasesEventContent, avatar::AvatarEventContent, canonical_alias::CanonicalAliasEventContent, create::CreateEventContent, guest_access::GuestAccessEventContent, history_visibility::HistoryVisibilityEventContent, join_rules::JoinRulesEventContent, member::MemberEventContent, name::NameEventContent, power_levels::PowerLevelsEventContent, third_party_invite::ThirdPartyInviteEventContent, topic::TopicEventContent, }, EventType, }; /// 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), } /// A "stripped-down" version of a core state event. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StrippedStateContent { /// Data specific to the event type. pub content: C, /// The type of the event. #[serde(rename = "type")] pub event_type: EventType, /// A key that determines which piece of room state the event represents. pub state_key: String, /// The unique identifier for the user who sent this event. pub sender: UserId, } impl Serialize for StrippedState { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { StrippedState::RoomAliases(ref event) => event.serialize(serializer), StrippedState::RoomAvatar(ref event) => event.serialize(serializer), StrippedState::RoomCanonicalAlias(ref event) => event.serialize(serializer), StrippedState::RoomCreate(ref event) => event.serialize(serializer), StrippedState::RoomGuestAccess(ref event) => event.serialize(serializer), StrippedState::RoomHistoryVisibility(ref event) => event.serialize(serializer), StrippedState::RoomJoinRules(ref event) => event.serialize(serializer), StrippedState::RoomMember(ref event) => event.serialize(serializer), StrippedState::RoomName(ref event) => event.serialize(serializer), StrippedState::RoomPowerLevels(ref event) => event.serialize(serializer), StrippedState::RoomThirdPartyInvite(ref event) => event.serialize(serializer), StrippedState::RoomTopic(ref event) => event.serialize(serializer), } } } impl<'de> Deserialize<'de> for StrippedState { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let value: Value = Deserialize::deserialize(deserializer)?; let event_type_value = match value.get("type") { Some(value) => value.clone(), None => return Err(D::Error::missing_field("type")), }; let event_type = match from_value::(event_type_value.clone()) { Ok(event_type) => event_type, Err(error) => return Err(D::Error::custom(error.to_string())), }; match event_type { EventType::RoomAliases => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomAliases(event)) } EventType::RoomAvatar => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomAvatar(event)) } EventType::RoomCanonicalAlias => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomCanonicalAlias(event)) } EventType::RoomCreate => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomCreate(event)) } EventType::RoomGuestAccess => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomGuestAccess(event)) } EventType::RoomHistoryVisibility => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomHistoryVisibility(event)) } EventType::RoomJoinRules => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomJoinRules(event)) } EventType::RoomMember => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomMember(event)) } EventType::RoomName => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomName(event)) } EventType::RoomPowerLevels => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomPowerLevels(event)) } EventType::RoomThirdPartyInvite => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomThirdPartyInvite(event)) } EventType::RoomTopic => { let event = match from_value::(value) { Ok(event) => event, Err(error) => return Err(D::Error::custom(error.to_string())), }; Ok(StrippedState::RoomTopic(event)) } _ => Err(D::Error::custom("not a state event".to_string())), } } } /// A stripped-down version of the *m.room.aliases* event. pub type StrippedRoomAliases = StrippedStateContent; /// A stripped-down version of the *m.room.avatar* event. pub type StrippedRoomAvatar = StrippedStateContent; /// A stripped-down version of the *m.room.canonical_alias* event. pub type StrippedRoomCanonicalAlias = StrippedStateContent; /// A stripped-down version of the *m.room.create* event. pub type StrippedRoomCreate = StrippedStateContent; /// 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; #[cfg(test)] mod tests { use std::convert::TryFrom; use ruma_identifiers::UserId; use serde_json::{from_str, to_string}; use super::{StrippedRoomTopic, StrippedState}; use crate::{ room::{join_rules::JoinRule, topic::TopicEventContent}, EventType, }; #[test] fn serialize_stripped_state_event() { let content = StrippedRoomTopic { content: TopicEventContent { topic: "Testing room".to_string(), }, state_key: "".to_string(), event_type: EventType::RoomTopic, sender: UserId::try_from("@example:localhost").unwrap(), }; let event = StrippedState::RoomTopic(content); assert_eq!( to_string(&event).unwrap(), r#"{"content":{"topic":"Testing room"},"type":"m.room.topic","state_key":"","sender":"@example:localhost"}"# ); } #[test] fn deserialize_stripped_state_events() { let name_event = r#"{ "type": "m.room.name", "state_key": "", "sender": "@example:localhost", "content": {"name": "Ruma"} }"#; let join_rules_event = r#"{ "type": "m.room.join_rules", "state_key": "", "sender": "@example:localhost", "content": { "join_rule": "public" } }"#; let avatar_event = r#"{ "type": "m.room.avatar", "state_key": "", "sender": "@example:localhost", "content": { "info": { "h": 128, "w": 128, "mimetype": "image/jpeg", "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, "w": 16, "mimetype": "image/jpeg", "size": 32 }, "thumbnail_url": "https://domain.com/image-thumbnail.jpg", "url": "https://domain.com/image.jpg" } }"#; match from_str::(name_event).unwrap() { StrippedState::RoomName(event) => { assert_eq!(event.content.name, Some("Ruma".to_string())); assert_eq!(event.event_type, EventType::RoomName); assert_eq!(event.state_key, ""); assert_eq!(event.sender.to_string(), "@example:localhost"); } _ => { unreachable!(); } }; match from_str::(join_rules_event).unwrap() { StrippedState::RoomJoinRules(event) => { assert_eq!(event.content.join_rule, JoinRule::Public); assert_eq!(event.event_type, EventType::RoomJoinRules); assert_eq!(event.state_key, ""); assert_eq!(event.sender.to_string(), "@example:localhost"); } _ => { unreachable!(); } }; match from_str::(avatar_event).unwrap() { StrippedState::RoomAvatar(event) => { let image_info = event.content.info.unwrap(); assert_eq!(image_info.height, 128); assert_eq!(image_info.width, 128); assert_eq!(image_info.mimetype, "image/jpeg"); assert_eq!(image_info.size, 1024); assert_eq!(image_info.thumbnail_info.unwrap().size, 32); assert_eq!(event.content.url, "https://domain.com/image.jpg"); assert_eq!(event.event_type, EventType::RoomAvatar); assert_eq!(event.state_key, ""); assert_eq!(event.sender.to_string(), "@example:localhost"); } _ => { unreachable!(); } }; } }