From 245bf75276212070fea5a1d5b00760f76e382906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 25 Mar 2022 18:14:11 +0100 Subject: [PATCH] events: Replace Captions with MessageContent Use a custom serde implementation --- crates/ruma-common/src/events/image.rs | 76 ++----------------- crates/ruma-common/src/events/message.rs | 2 +- .../src/events/message/content_serde.rs | 31 ++++++++ crates/ruma-common/src/events/video.rs | 14 ++-- crates/ruma-common/tests/events/image.rs | 12 ++- crates/ruma-common/tests/events/video.rs | 12 ++- 6 files changed, 58 insertions(+), 89 deletions(-) diff --git a/crates/ruma-common/src/events/image.rs b/crates/ruma-common/src/events/image.rs index 395e314d..c615cdb5 100644 --- a/crates/ruma-common/src/events/image.rs +++ b/crates/ruma-common/src/events/image.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use super::{ file::{EncryptedContent, FileContent}, - message::{MessageContent, Text}, + message::MessageContent, room::message::Relation, }; use crate::MxcUri; @@ -35,8 +35,13 @@ pub struct ImageEventContent { pub thumbnail: Vec, /// The captions of the message. - #[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")] - pub caption: Captions, + #[serde( + rename = "m.caption", + with = "super::message::content_serde::as_vec", + default, + skip_serializing_if = "Option::is_none" + )] + pub caption: Option, /// Information about related messages for [rich replies]. /// @@ -151,71 +156,6 @@ impl ThumbnailContent { } } -/// An array of captions. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Captions(pub(crate) Vec); - -impl Captions { - /// Creates a new `Captions` with the given captions. - /// - /// The captions must be ordered by most preferred first. - pub fn new(captions: &[Text]) -> Self { - Self(captions.to_owned()) - } - - /// A convenience constructor to create a plain text caption. - pub fn plain(body: impl Into) -> Self { - Self(vec![Text::plain(body)]) - } - - /// A convenience constructor to create an HTML caption. - pub fn html(body: impl Into, html_body: impl Into) -> Self { - Self(vec![Text::html(html_body), Text::plain(body)]) - } - - /// A convenience constructor to create a Markdown caption. - /// - /// Returns an HTML caption if some Markdown formatting was detected, otherwise returns a plain - /// text caption. - #[cfg(feature = "markdown")] - pub fn markdown(body: impl AsRef + Into) -> Self { - let mut message = Vec::with_capacity(2); - if let Some(html_body) = Text::markdown(&body) { - message.push(html_body); - } - message.push(Text::plain(body)); - Self(message) - } - - /// Get the captions. - /// - /// The captions are ordered by most preferred first. - pub fn captions(&self) -> &[Text] { - &self.0 - } - - /// Whether this is empty. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Get the plain text representation of this caption. - pub fn find_plain(&self) -> Option<&str> { - self.captions() - .iter() - .find(|content| content.mimetype == "text/plain") - .map(|content| content.body.as_ref()) - } - - /// Get the HTML representation of this caption. - pub fn find_html(&self) -> Option<&str> { - self.captions() - .iter() - .find(|content| content.mimetype == "text/html") - .map(|content| content.body.as_ref()) - } -} - /// Image content. #[derive(Default, Clone, Debug, Serialize, Deserialize)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] diff --git a/crates/ruma-common/src/events/message.rs b/crates/ruma-common/src/events/message.rs index 70a50ed9..f5df5a2c 100644 --- a/crates/ruma-common/src/events/message.rs +++ b/crates/ruma-common/src/events/message.rs @@ -52,7 +52,7 @@ use std::ops::Deref; use ruma_macros::EventContent; use serde::{Deserialize, Serialize}; -mod content_serde; +pub(crate) mod content_serde; use content_serde::MessageContentSerDeHelper; diff --git a/crates/ruma-common/src/events/message/content_serde.rs b/crates/ruma-common/src/events/message/content_serde.rs index f664d4f6..70747d88 100644 --- a/crates/ruma-common/src/events/message/content_serde.rs +++ b/crates/ruma-common/src/events/message/content_serde.rs @@ -67,3 +67,34 @@ impl Serialize for MessageContent { st.end() } } + +pub(crate) mod as_vec { + use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serializer}; + + use crate::events::message::{MessageContent, Text}; + + /// Serializes a `Option` as a `Vec`. + pub fn serialize(content: &Option, serializer: S) -> Result + where + S: Serializer, + { + if let Some(content) = content { + let mut seq = serializer.serialize_seq(Some(content.len()))?; + for e in content.iter() { + seq.serialize_element(e)?; + } + seq.end() + } else { + serializer.serialize_seq(Some(0))?.end() + } + } + + /// Deserializes a `Vec` to an `Option`. + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + Option::>::deserialize(deserializer) + .map(|content| content.filter(|content| !content.is_empty()).map(Into::into)) + } +} diff --git a/crates/ruma-common/src/events/video.rs b/crates/ruma-common/src/events/video.rs index 637afc37..c4018403 100644 --- a/crates/ruma-common/src/events/video.rs +++ b/crates/ruma-common/src/events/video.rs @@ -9,10 +9,7 @@ use ruma_macros::EventContent; use serde::{Deserialize, Serialize}; use super::{ - file::FileContent, - image::{Captions, ThumbnailContent}, - message::MessageContent, - room::message::Relation, + file::FileContent, image::ThumbnailContent, message::MessageContent, room::message::Relation, }; /// The payload for an extensible video message. @@ -37,8 +34,13 @@ pub struct VideoEventContent { pub thumbnail: Vec, /// The captions of the message. - #[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")] - pub caption: Captions, + #[serde( + rename = "m.caption", + with = "super::message::content_serde::as_vec", + default, + skip_serializing_if = "Option::is_none" + )] + pub caption: Option, /// Information about related messages. #[serde(flatten, skip_serializing_if = "Option::is_none")] diff --git a/crates/ruma-common/tests/events/image.rs b/crates/ruma-common/tests/events/image.rs index 7d285bfd..110d546d 100644 --- a/crates/ruma-common/tests/events/image.rs +++ b/crates/ruma-common/tests/events/image.rs @@ -8,7 +8,7 @@ use ruma_common::{ events::{ file::{EncryptedContentInit, FileContent, FileContentInfo}, image::{ - Captions, ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent, + ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo, }, message::MessageContent, @@ -128,7 +128,7 @@ fn image_event_serialization() { ), None )], - caption: Captions::plain("This is my house"), + caption: Some(MessageContent::plain("This is my house")), relates_to: Some(Relation::Reply { in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()), }), @@ -207,7 +207,7 @@ fn plain_content_deserialization() { assert_matches!( from_json_value::(json_data) .unwrap(), - ImageEventContent { message, file, image, thumbnail, caption, .. } + ImageEventContent { message, file, image, thumbnail, caption: Some(caption), .. } if message.find_plain() == Some("Upload: my_cat.png") && message.find_html().is_none() && file.url == "mxc://notareal.hs/abcdef" @@ -248,7 +248,7 @@ fn encrypted_content_deserialization() { assert_matches!( from_json_value::(json_data) .unwrap(), - ImageEventContent { message, file, image, thumbnail, caption, .. } + ImageEventContent { message, file, image, thumbnail, caption: None, .. } if message.find_plain() == Some("Upload: my_file.txt") && message.find_html().is_none() && file.url == "mxc://notareal.hs/abcdef" @@ -256,7 +256,6 @@ fn encrypted_content_deserialization() { && image.width.is_none() && image.height.is_none() && thumbnail[0].file.url == "mxc://notareal.hs/thumbnail" - && caption.is_empty() ); } @@ -295,7 +294,7 @@ fn message_event_deserialization() { }, image, thumbnail, - caption, + caption: None, .. }, event_id, @@ -313,7 +312,6 @@ fn message_event_deserialization() { && image.width == Some(uint!(1300)) && image.height == Some(uint!(837)) && thumbnail.is_empty() - && caption.is_empty() && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848)) && room_id == room_id!("!roomid:notareal.hs") && sender == user_id!("@user:notareal.hs") diff --git a/crates/ruma-common/tests/events/video.rs b/crates/ruma-common/tests/events/video.rs index 2a6f732f..4778b18b 100644 --- a/crates/ruma-common/tests/events/video.rs +++ b/crates/ruma-common/tests/events/video.rs @@ -9,7 +9,7 @@ use ruma_common::{ event_id, events::{ file::{EncryptedContentInit, FileContent, FileContentInfo}, - image::{Captions, ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo}, + image::{ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo}, message::MessageContent, room::{ message::{InReplyTo, Relation}, @@ -135,7 +135,7 @@ fn event_serialization() { ), None )], - caption: Captions::plain("This is my awesome vintage lava lamp"), + caption: Some(MessageContent::plain("This is my awesome vintage lava lamp")), relates_to: Some(Relation::Reply { in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()), }), @@ -215,7 +215,7 @@ fn plain_content_deserialization() { assert_matches!( from_json_value::(json_data) .unwrap(), - VideoEventContent { message, file, video, thumbnail, caption, .. } + VideoEventContent { message, file, video, thumbnail, caption: Some(caption), .. } if message.find_plain() == Some("Video: my_cat.mp4") && message.find_html().is_none() && file.url == "mxc://notareal.hs/abcdef" @@ -257,7 +257,7 @@ fn encrypted_content_deserialization() { assert_matches!( from_json_value::(json_data) .unwrap(), - VideoEventContent { message, file, video, thumbnail, caption, .. } + VideoEventContent { message, file, video, thumbnail, caption: None, .. } if message.find_plain() == Some("Upload: my_cat.mp4") && message.find_html().is_none() && file.url == "mxc://notareal.hs/abcdef" @@ -266,7 +266,6 @@ fn encrypted_content_deserialization() { && video.height.is_none() && video.duration.is_none() && thumbnail[0].file.url == "mxc://notareal.hs/thumbnail" - && caption.is_empty() ); } @@ -305,7 +304,7 @@ fn message_event_deserialization() { }, video, thumbnail, - caption, + caption: None, .. }, event_id, @@ -324,7 +323,6 @@ fn message_event_deserialization() { && video.height == Some(uint!(837)) && video.duration.is_none() && thumbnail.is_empty() - && caption.is_empty() && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848)) && room_id == room_id!("!roomid:notareal.hs") && sender == user_id!("@user:notareal.hs")