From f77b49d4b8e383f01eea2e3c23572e2f046e8c93 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 15 May 2021 22:20:48 +0200 Subject: [PATCH] events: Make more types non-exhaustive in room::message --- crates/ruma-events/src/room/message.rs | 55 +++++++++++++++++++++--- crates/ruma-events/tests/room_message.rs | 23 +++++----- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/crates/ruma-events/src/room/message.rs b/crates/ruma-events/src/room/message.rs index 5ed7737a..d26aacc1 100644 --- a/crates/ruma-events/src/room/message.rs +++ b/crates/ruma-events/src/room/message.rs @@ -343,27 +343,42 @@ impl From for Relation { /// The payload for an audio message. #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[serde(tag = "msgtype", rename = "m.audio")] pub struct AudioMessageEventContent { /// The textual representation of this message. pub body: String, - /// Metadata for the audio clip referred to in `url`. - #[serde(skip_serializing_if = "Option::is_none")] - pub info: Option>, - /// The URL to the audio clip. Required if the file is unencrypted. The URL (typically /// [MXC URI](https://matrix.org/docs/spec/client_server/r0.6.1#mxc-uri)) to the audio clip. #[serde(skip_serializing_if = "Option::is_none")] pub url: Option, + /// Metadata for the audio clip referred to in `url`. + #[serde(skip_serializing_if = "Option::is_none")] + pub info: Option>, + /// Required if the audio clip is encrypted. Information on the encrypted audio clip. #[serde(skip_serializing_if = "Option::is_none")] pub file: Option>, } +impl AudioMessageEventContent { + /// Creates a new non-encrypted `AudioMessageEventContent` with the given body, url and optional + /// extra info. + pub fn plain(body: String, url: MxcUri, info: Option>) -> Self { + Self { body, url: Some(url), info, file: None } + } + + /// Creates a new encrypted `AudioMessageEventContent` with the given body and encrypted file. + pub fn encrypted(body: String, file: EncryptedFile) -> Self { + Self { body, url: None, info: None, file: Some(Box::new(file)) } + } +} + /// Metadata about an audio clip. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub struct AudioInfo { /// The duration of the audio in milliseconds. #[serde(skip_serializing_if = "Option::is_none")] @@ -378,8 +393,16 @@ pub struct AudioInfo { pub size: Option, } +impl AudioInfo { + /// Creates an empty `AudioInfo`. + pub fn new() -> Self { + Self::default() + } +} + /// The payload for an emote message. #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[serde(tag = "msgtype", rename = "m.emote")] pub struct EmoteMessageEventContent { /// The emote action to perform. @@ -390,6 +413,28 @@ pub struct EmoteMessageEventContent { pub formatted: Option, } +impl EmoteMessageEventContent { + /// A convenience constructor to create a plain-text emote. + pub fn plain(body: impl Into) -> Self { + Self { body: body.into(), formatted: None } + } + + /// A convenience constructor to create an html emote message. + pub fn html(body: impl Into, html_body: impl Into) -> Self { + Self { formatted: Some(FormattedBody::html(html_body)), ..Self::plain(body) } + } + + /// A convenience constructor to create a markdown emote. + #[cfg(feature = "markdown")] + #[cfg_attr(docsrs, doc(cfg(feature = "markdown")))] + pub fn markdown(body: impl Into) -> Self { + let body = body.into(); + let mut html_body = String::new(); + pulldown_cmark::html::push_html(&mut html_body, pulldown_cmark::Parser::new(&body)); + Self::html(body, html_body) + } +} + /// The payload for a file message. #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(tag = "msgtype", rename = "m.file")] diff --git a/crates/ruma-events/tests/room_message.rs b/crates/ruma-events/tests/room_message.rs index b6e716e3..ec7b0af7 100644 --- a/crates/ruma-events/tests/room_message.rs +++ b/crates/ruma-events/tests/room_message.rs @@ -37,12 +37,11 @@ macro_rules! json_object { #[test] fn serialization() { let ev = MessageEvent { - content: MessageEventContent::new(MessageType::Audio(AudioMessageEventContent { - body: "test".into(), - info: None, - url: Some(mxc_uri!("mxc://example.org/ffed755USFFxlgbQYZGtryd")), - file: None, - })), + content: MessageEventContent::new(MessageType::Audio(AudioMessageEventContent::plain( + "test".into(), + mxc_uri!("mxc://example.org/ffed755USFFxlgbQYZGtryd"), + None, + ))), event_id: event_id!("$143273582443PhrSn:example.org"), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)), room_id: room_id!("!testroomid:example.org"), @@ -70,12 +69,11 @@ fn serialization() { #[test] fn content_serialization() { let message_event_content = - MessageEventContent::new(MessageType::Audio(AudioMessageEventContent { - body: "test".into(), - info: None, - url: Some(mxc_uri!("mxc://example.org/ffed755USFFxlgbQYZGtryd")), - file: None, - })); + MessageEventContent::new(MessageType::Audio(AudioMessageEventContent::plain( + "test".into(), + mxc_uri!("mxc://example.org/ffed755USFFxlgbQYZGtryd"), + None, + ))); assert_eq!( to_json_value(&message_event_content).unwrap(), @@ -346,6 +344,7 @@ fn content_deserialization() { info: None, url: Some(url), file: None, + .. }), .. } if body == "test" && url.to_string() == "mxc://example.org/ffed755USFFxlgbQYZGtryd"