events: Allow to create custom MessageEventContents

This commit is contained in:
Kévin Commaille 2022-07-02 10:40:43 +02:00 committed by Kévin Commaille
parent ca8c66c885
commit ed8990c3fd
2 changed files with 56 additions and 10 deletions

View File

@ -68,6 +68,9 @@ use super::room::message::{
/// This is the new primary type introduced in [MSC1767] and should not be sent before the end of /// This is the new primary type introduced in [MSC1767] and should not be sent before the end of
/// the transition period. See the documentation of the [`message`] module for more information. /// the transition period. See the documentation of the [`message`] module for more information.
/// ///
/// To construct a `MessageEventContent` with a custom [`MessageContent`], convert it with
/// `MessageEventContent::from()` / `.into()`.
///
/// `MessageEventContent` can be converted to a [`RoomMessageEventContent`] with a /// `MessageEventContent` can be converted to a [`RoomMessageEventContent`] with a
/// [`MessageType::Text`]. You can convert it back with /// [`MessageType::Text`]. You can convert it back with
/// [`MessageEventContent::from_text_room_message()`]. /// [`MessageEventContent::from_text_room_message()`].
@ -124,6 +127,12 @@ impl MessageEventContent {
} }
} }
impl From<MessageContent> for MessageEventContent {
fn from(message: MessageContent) -> Self {
Self { message, relates_to: None }
}
}
impl From<MessageEventContent> for RoomMessageEventContent { impl From<MessageEventContent> for RoomMessageEventContent {
fn from(content: MessageEventContent) -> Self { fn from(content: MessageEventContent) -> Self {
let MessageEventContent { message, relates_to, .. } = content; let MessageEventContent { message, relates_to, .. } = content;
@ -136,6 +145,9 @@ impl From<MessageEventContent> for RoomMessageEventContent {
/// ///
/// A `MessageContent` must contain at least one message to be used as a fallback text /// A `MessageContent` must contain at least one message to be used as a fallback text
/// representation. /// representation.
///
/// To construct a `MessageContent` with custom MIME types, construct a `Vec<Text>` first and use
/// its `.try_from()` / `.try_into()` implementation that will only fail if the `Vec` is empty.
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
#[serde(try_from = "MessageContentSerDeHelper")] #[serde(try_from = "MessageContentSerDeHelper")]
pub struct MessageContent(pub(crate) Vec<Text>); pub struct MessageContent(pub(crate) Vec<Text>);
@ -231,7 +243,11 @@ impl Deref for MessageContent {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Text { pub struct Text {
/// The mime type of the `body`. /// The MIME type of the `body`.
///
/// This must follow the format defined in [RFC6838].
///
/// [RFC6838]: https://datatracker.ietf.org/doc/html/rfc6838
#[serde(default = "Text::default_mimetype")] #[serde(default = "Text::default_mimetype")]
pub mimetype: String, pub mimetype: String,
@ -249,24 +265,24 @@ pub struct Text {
} }
impl Text { impl Text {
/// Creates a new plain text message body. /// Creates a new `Text` with the given MIME type and body.
pub fn plain(body: impl Into<String>) -> Self { pub fn new(mimetype: impl Into<String>, body: impl Into<String>) -> Self {
Self { Self {
mimetype: "text/plain".to_owned(), mimetype: mimetype.into(),
body: body.into(), body: body.into(),
#[cfg(feature = "unstable-msc3554")] #[cfg(feature = "unstable-msc3554")]
lang: None, lang: None,
} }
} }
/// Creates a new plain text message body.
pub fn plain(body: impl Into<String>) -> Self {
Self::new("text/plain", body)
}
/// Creates a new HTML-formatted message body. /// Creates a new HTML-formatted message body.
pub fn html(body: impl Into<String>) -> Self { pub fn html(body: impl Into<String>) -> Self {
Self { Self::new("text/html", body)
mimetype: "text/html".to_owned(),
body: body.into(),
#[cfg(feature = "unstable-msc3554")]
lang: None,
}
} }
/// Creates a new HTML-formatted message body by parsing the Markdown in `body`. /// Creates a new HTML-formatted message body by parsing the Markdown in `body`.

View File

@ -58,6 +58,36 @@ fn plain_text_content_serialization() {
); );
} }
#[test]
fn unknown_mimetype_content_serialization() {
let message_event_content = MessageEventContent::from(
MessageContent::try_from(vec![
Text::plain("> <@test:example.com> test\n\ntest reply"),
Text::new(
"application/json",
r#"{ "quote": "<@test:example.com> test", "reply": "test reply" }"#,
),
])
.unwrap(),
);
assert_eq!(
to_json_value(&message_event_content).unwrap(),
json!({
"org.matrix.msc1767.message": [
{
"body": "> <@test:example.com> test\n\ntest reply",
"mimetype": "text/plain",
},
{
"body": r#"{ "quote": "<@test:example.com> test", "reply": "test reply" }"#,
"mimetype": "application/json",
},
]
})
);
}
#[test] #[test]
#[cfg(feature = "markdown")] #[cfg(feature = "markdown")]
fn markdown_content_serialization() { fn markdown_content_serialization() {