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::{
|
use super::{
|
||||||
file::{EncryptedContent, FileContent},
|
file::{EncryptedContent, FileContent},
|
||||||
message::{MessageContent, Text},
|
message::MessageContent,
|
||||||
room::message::Relation,
|
room::message::Relation,
|
||||||
};
|
};
|
||||||
use crate::MxcUri;
|
use crate::MxcUri;
|
||||||
@ -35,8 +35,13 @@ pub struct ImageEventContent {
|
|||||||
pub thumbnail: Vec<ThumbnailContent>,
|
pub thumbnail: Vec<ThumbnailContent>,
|
||||||
|
|
||||||
/// The captions of the message.
|
/// The captions of the message.
|
||||||
#[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")]
|
#[serde(
|
||||||
pub caption: Captions,
|
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].
|
/// 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.
|
/// Image content.
|
||||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
@ -52,7 +52,7 @@ use std::ops::Deref;
|
|||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod content_serde;
|
pub(crate) mod content_serde;
|
||||||
|
|
||||||
use content_serde::MessageContentSerDeHelper;
|
use content_serde::MessageContentSerDeHelper;
|
||||||
|
|
||||||
|
@ -67,3 +67,34 @@ impl Serialize for MessageContent {
|
|||||||
st.end()
|
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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
file::FileContent,
|
file::FileContent, image::ThumbnailContent, message::MessageContent, room::message::Relation,
|
||||||
image::{Captions, ThumbnailContent},
|
|
||||||
message::MessageContent,
|
|
||||||
room::message::Relation,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The payload for an extensible video message.
|
/// The payload for an extensible video message.
|
||||||
@ -37,8 +34,13 @@ pub struct VideoEventContent {
|
|||||||
pub thumbnail: Vec<ThumbnailContent>,
|
pub thumbnail: Vec<ThumbnailContent>,
|
||||||
|
|
||||||
/// The captions of the message.
|
/// The captions of the message.
|
||||||
#[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")]
|
#[serde(
|
||||||
pub caption: Captions,
|
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.
|
/// Information about related messages.
|
||||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -8,7 +8,7 @@ use ruma_common::{
|
|||||||
events::{
|
events::{
|
||||||
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||||
image::{
|
image::{
|
||||||
Captions, ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent,
|
ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent,
|
||||||
ThumbnailFileContentInfo,
|
ThumbnailFileContentInfo,
|
||||||
},
|
},
|
||||||
message::MessageContent,
|
message::MessageContent,
|
||||||
@ -128,7 +128,7 @@ fn image_event_serialization() {
|
|||||||
),
|
),
|
||||||
None
|
None
|
||||||
)],
|
)],
|
||||||
caption: Captions::plain("This is my house"),
|
caption: Some(MessageContent::plain("This is my house")),
|
||||||
relates_to: Some(Relation::Reply {
|
relates_to: Some(Relation::Reply {
|
||||||
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||||
}),
|
}),
|
||||||
@ -207,7 +207,7 @@ fn plain_content_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<ImageEventContent>(json_data)
|
from_json_value::<ImageEventContent>(json_data)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
ImageEventContent { message, file, image, thumbnail, caption, .. }
|
ImageEventContent { message, file, image, thumbnail, caption: Some(caption), .. }
|
||||||
if message.find_plain() == Some("Upload: my_cat.png")
|
if message.find_plain() == Some("Upload: my_cat.png")
|
||||||
&& message.find_html().is_none()
|
&& message.find_html().is_none()
|
||||||
&& file.url == "mxc://notareal.hs/abcdef"
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
@ -248,7 +248,7 @@ fn encrypted_content_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<ImageEventContent>(json_data)
|
from_json_value::<ImageEventContent>(json_data)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
ImageEventContent { message, file, image, thumbnail, caption, .. }
|
ImageEventContent { message, file, image, thumbnail, caption: None, .. }
|
||||||
if message.find_plain() == Some("Upload: my_file.txt")
|
if message.find_plain() == Some("Upload: my_file.txt")
|
||||||
&& message.find_html().is_none()
|
&& message.find_html().is_none()
|
||||||
&& file.url == "mxc://notareal.hs/abcdef"
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
@ -256,7 +256,6 @@ fn encrypted_content_deserialization() {
|
|||||||
&& image.width.is_none()
|
&& image.width.is_none()
|
||||||
&& image.height.is_none()
|
&& image.height.is_none()
|
||||||
&& thumbnail[0].file.url == "mxc://notareal.hs/thumbnail"
|
&& thumbnail[0].file.url == "mxc://notareal.hs/thumbnail"
|
||||||
&& caption.is_empty()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +294,7 @@ fn message_event_deserialization() {
|
|||||||
},
|
},
|
||||||
image,
|
image,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
caption,
|
caption: None,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
event_id,
|
event_id,
|
||||||
@ -313,7 +312,6 @@ fn message_event_deserialization() {
|
|||||||
&& image.width == Some(uint!(1300))
|
&& image.width == Some(uint!(1300))
|
||||||
&& image.height == Some(uint!(837))
|
&& image.height == Some(uint!(837))
|
||||||
&& thumbnail.is_empty()
|
&& thumbnail.is_empty()
|
||||||
&& caption.is_empty()
|
|
||||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
||||||
&& room_id == room_id!("!roomid:notareal.hs")
|
&& room_id == room_id!("!roomid:notareal.hs")
|
||||||
&& sender == user_id!("@user:notareal.hs")
|
&& sender == user_id!("@user:notareal.hs")
|
||||||
|
@ -9,7 +9,7 @@ use ruma_common::{
|
|||||||
event_id,
|
event_id,
|
||||||
events::{
|
events::{
|
||||||
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||||
image::{Captions, ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo},
|
image::{ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo},
|
||||||
message::MessageContent,
|
message::MessageContent,
|
||||||
room::{
|
room::{
|
||||||
message::{InReplyTo, Relation},
|
message::{InReplyTo, Relation},
|
||||||
@ -135,7 +135,7 @@ fn event_serialization() {
|
|||||||
),
|
),
|
||||||
None
|
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 {
|
relates_to: Some(Relation::Reply {
|
||||||
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||||
}),
|
}),
|
||||||
@ -215,7 +215,7 @@ fn plain_content_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<VideoEventContent>(json_data)
|
from_json_value::<VideoEventContent>(json_data)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
VideoEventContent { message, file, video, thumbnail, caption, .. }
|
VideoEventContent { message, file, video, thumbnail, caption: Some(caption), .. }
|
||||||
if message.find_plain() == Some("Video: my_cat.mp4")
|
if message.find_plain() == Some("Video: my_cat.mp4")
|
||||||
&& message.find_html().is_none()
|
&& message.find_html().is_none()
|
||||||
&& file.url == "mxc://notareal.hs/abcdef"
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
@ -257,7 +257,7 @@ fn encrypted_content_deserialization() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
from_json_value::<VideoEventContent>(json_data)
|
from_json_value::<VideoEventContent>(json_data)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
VideoEventContent { message, file, video, thumbnail, caption, .. }
|
VideoEventContent { message, file, video, thumbnail, caption: None, .. }
|
||||||
if message.find_plain() == Some("Upload: my_cat.mp4")
|
if message.find_plain() == Some("Upload: my_cat.mp4")
|
||||||
&& message.find_html().is_none()
|
&& message.find_html().is_none()
|
||||||
&& file.url == "mxc://notareal.hs/abcdef"
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
@ -266,7 +266,6 @@ fn encrypted_content_deserialization() {
|
|||||||
&& video.height.is_none()
|
&& video.height.is_none()
|
||||||
&& video.duration.is_none()
|
&& video.duration.is_none()
|
||||||
&& thumbnail[0].file.url == "mxc://notareal.hs/thumbnail"
|
&& thumbnail[0].file.url == "mxc://notareal.hs/thumbnail"
|
||||||
&& caption.is_empty()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +304,7 @@ fn message_event_deserialization() {
|
|||||||
},
|
},
|
||||||
video,
|
video,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
caption,
|
caption: None,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
event_id,
|
event_id,
|
||||||
@ -324,7 +323,6 @@ fn message_event_deserialization() {
|
|||||||
&& video.height == Some(uint!(837))
|
&& video.height == Some(uint!(837))
|
||||||
&& video.duration.is_none()
|
&& video.duration.is_none()
|
||||||
&& thumbnail.is_empty()
|
&& thumbnail.is_empty()
|
||||||
&& caption.is_empty()
|
|
||||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
||||||
&& room_id == room_id!("!roomid:notareal.hs")
|
&& room_id == room_id!("!roomid:notareal.hs")
|
||||||
&& sender == user_id!("@user:notareal.hs")
|
&& sender == user_id!("@user:notareal.hs")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user