events: Replace Captions with MessageContent
Use a custom serde implementation
This commit is contained in:
parent
93b4114a82
commit
245bf75276
@ -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<ThumbnailContent>,
|
||||
|
||||
/// 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<MessageContent>,
|
||||
|
||||
/// 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<Text>);
|
||||
|
||||
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<String>) -> Self {
|
||||
Self(vec![Text::plain(body)])
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML caption.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> 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<str> + Into<String>) -> 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)]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<MessageContent>` as a `Vec<Text>`.
|
||||
pub fn serialize<S>(content: &Option<MessageContent>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<Text>` to an `Option<MessageContent>`.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<MessageContent>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Option::<Vec<Text>>::deserialize(deserializer)
|
||||
.map(|content| content.filter(|content| !content.is_empty()).map(Into::into))
|
||||
}
|
||||
}
|
||||
|
@ -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<ThumbnailContent>,
|
||||
|
||||
/// 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<MessageContent>,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
|
@ -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::<ImageEventContent>(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::<ImageEventContent>(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")
|
||||
|
@ -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::<VideoEventContent>(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::<VideoEventContent>(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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user