events: Update types according to changes in MSCs 1767, 3954, 3955 and 3956
This commit is contained in:
parent
ba237a9cfd
commit
8477efb2ef
@ -38,12 +38,15 @@ unstable-msc3245 = ["unstable-msc3246"]
|
||||
unstable-msc3246 = ["unstable-msc3551"]
|
||||
unstable-msc3381 = ["unstable-msc1767"]
|
||||
unstable-msc3488 = ["unstable-msc1767"]
|
||||
unstable-msc3551 = ["unstable-msc1767"]
|
||||
unstable-msc3551 = ["unstable-msc3956"]
|
||||
unstable-msc3552 = ["unstable-msc3551"]
|
||||
unstable-msc3553 = ["unstable-msc3552"]
|
||||
unstable-msc3554 = ["unstable-msc1767"]
|
||||
unstable-msc3931 = []
|
||||
unstable-msc3932 = ["unstable-msc3931"]
|
||||
unstable-msc3954 = ["unstable-msc1767"]
|
||||
unstable-msc3955 = ["unstable-msc1767"]
|
||||
unstable-msc3956 = ["unstable-msc1767"]
|
||||
unstable-pdu = []
|
||||
unstable-sanitize = ["dep:html5ever", "dep:phf"]
|
||||
unstable-unspecified = []
|
||||
|
@ -125,8 +125,10 @@ pub mod audio;
|
||||
pub mod call;
|
||||
pub mod direct;
|
||||
pub mod dummy;
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[cfg(feature = "unstable-msc3954")]
|
||||
pub mod emote;
|
||||
#[cfg(feature = "unstable-msc3956")]
|
||||
pub mod encrypted;
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
pub mod file;
|
||||
pub mod forwarded_room_key;
|
||||
@ -140,8 +142,6 @@ pub mod key;
|
||||
pub mod location;
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
pub mod message;
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
pub mod notice;
|
||||
#[cfg(feature = "unstable-pdu")]
|
||||
pub mod pdu;
|
||||
pub mod policy;
|
||||
|
@ -13,12 +13,12 @@ mod waveform_serde;
|
||||
|
||||
use waveform_serde::WaveformSerDeHelper;
|
||||
|
||||
use super::{file::FileContent, message::MessageContent, room::message::Relation};
|
||||
use super::{file::FileContent, message::TextContentBlock, room::message::Relation};
|
||||
|
||||
/// The payload for an extensible audio message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3246] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3246] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC3246]: https://github.com/matrix-org/matrix-spec-proposals/pull/3246
|
||||
/// [`message`]: super::message
|
||||
@ -26,9 +26,9 @@ use super::{file::FileContent, message::MessageContent, room::message::Relation}
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.audio", kind = MessageLike, without_relation)]
|
||||
pub struct AudioEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
/// The text representations of the message.
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "m.file")]
|
||||
@ -48,20 +48,21 @@ pub struct AudioEventContent {
|
||||
}
|
||||
|
||||
impl AudioEventContent {
|
||||
/// Creates a new `AudioEventContent` with the given plain text message and file.
|
||||
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||
/// Creates a new `AudioEventContent` with the given text fallback and file.
|
||||
pub fn new(text: TextContentBlock, file: FileContent) -> Self {
|
||||
Self { text, file, audio: Default::default(), relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new `AudioEventContent` with the given plain text fallback representation and
|
||||
/// file.
|
||||
pub fn plain(text_fallback: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text: TextContentBlock::plain(text_fallback),
|
||||
file,
|
||||
audio: Default::default(),
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `AudioEventContent` with the given message and file.
|
||||
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||
Self { message, file, audio: Default::default(), relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Audio content.
|
||||
|
@ -1,26 +1,38 @@
|
||||
//! Types for extensible emote message events ([MSC1767]).
|
||||
//! Types for extensible emote message events ([MSC3954]).
|
||||
//!
|
||||
//! [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
//! [MSC3954]: https://github.com/matrix-org/matrix-spec-proposals/pull/3954
|
||||
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{message::MessageContent, room::message::Relation};
|
||||
use super::{message::TextContentBlock, room::message::Relation};
|
||||
|
||||
/// The payload for an extensible emote message.
|
||||
///
|
||||
/// 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.
|
||||
/// This is the new primary type introduced in [MSC3954] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
/// To construct an `EmoteEventContent` with a custom [`TextContentBlock`], convert it with
|
||||
/// `EmoteEventContent::from()` / `.into()`.
|
||||
///
|
||||
/// [MSC3954]: https://github.com/matrix-org/matrix-spec-proposals/pull/3954
|
||||
/// [`message`]: super::message
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.emote", kind = MessageLike, without_relation)]
|
||||
#[ruma_event(type = "org.matrix.msc1767.emote", kind = MessageLike, without_relation)]
|
||||
pub struct EmoteEventContent {
|
||||
/// The message's text content.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// Whether this message is automated.
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "crate::serde::is_default",
|
||||
rename = "org.matrix.msc1767.automated"
|
||||
)]
|
||||
pub automated: bool,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(
|
||||
@ -34,20 +46,46 @@ pub struct EmoteEventContent {
|
||||
impl EmoteEventContent {
|
||||
/// A convenience constructor to create a plain text emote.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::plain(body), relates_to: None }
|
||||
Self {
|
||||
text: TextContentBlock::plain(body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML emote.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::html(body, html_body), relates_to: None }
|
||||
Self {
|
||||
text: TextContentBlock::html(body, html_body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a Markdown emote.
|
||||
/// A convenience constructor to create an emote from Markdown.
|
||||
///
|
||||
/// Returns an HTML emote if some Markdown formatting was detected, otherwise returns a plain
|
||||
/// text emote.
|
||||
/// The content includes an HTML message if some Markdown formatting was detected, otherwise
|
||||
/// only a plain text message is included.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
Self { message: MessageContent::markdown(body), relates_to: None }
|
||||
Self {
|
||||
text: TextContentBlock::markdown(body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextContentBlock> for EmoteEventContent {
|
||||
fn from(text: TextContentBlock) -> Self {
|
||||
Self {
|
||||
text,
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
crates/ruma-common/src/events/encrypted.rs
Normal file
60
crates/ruma-common/src/events/encrypted.rs
Normal file
@ -0,0 +1,60 @@
|
||||
//! Types for extensible encrypted events ([MSC3956]).
|
||||
//!
|
||||
//! [MSC3956]: https://github.com/matrix-org/matrix-spec-proposals/pull/3956
|
||||
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::room::encrypted::{EncryptedEventScheme, Relation};
|
||||
|
||||
/// The payload for an extensible encrypted message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3956] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC3956]: https://github.com/matrix-org/matrix-spec-proposals/pull/3956
|
||||
/// [`message`]: super::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "org.matrix.msc1767.encrypted", kind = MessageLike)]
|
||||
pub struct EncryptedEventContent {
|
||||
/// The encrypted content.
|
||||
#[serde(rename = "org.matrix.msc1767.encrypted")]
|
||||
pub encrypted: EncryptedContentBlock,
|
||||
|
||||
/// Information about related events.
|
||||
#[serde(
|
||||
flatten,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "super::room::encrypted::relation_serde::deserialize_relation"
|
||||
)]
|
||||
pub relates_to: Option<Relation>,
|
||||
}
|
||||
|
||||
impl EncryptedEventContent {
|
||||
/// Creates a new `EncryptedEventContent` with the given scheme and relation.
|
||||
pub fn new(scheme: EncryptedEventScheme, relates_to: Option<Relation>) -> Self {
|
||||
Self { encrypted: scheme.into(), relates_to }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EncryptedEventScheme> for EncryptedEventContent {
|
||||
fn from(scheme: EncryptedEventScheme) -> Self {
|
||||
Self { encrypted: scheme.into(), relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// A block for encrypted content.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct EncryptedContentBlock {
|
||||
/// Algorithm-specific fields.
|
||||
#[serde(flatten)]
|
||||
pub scheme: EncryptedEventScheme,
|
||||
}
|
||||
|
||||
impl From<EncryptedEventScheme> for EncryptedContentBlock {
|
||||
fn from(scheme: EncryptedEventScheme) -> Self {
|
||||
Self { scheme }
|
||||
}
|
||||
}
|
@ -45,8 +45,12 @@ event_enum! {
|
||||
"m.call.reject" => super::call::reject,
|
||||
#[cfg(feature = "unstable-msc2746")]
|
||||
"m.call.select_answer" => super::call::select_answer,
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
"m.emote" => super::emote,
|
||||
#[cfg(feature = "unstable-msc3954")]
|
||||
#[ruma_enum(alias = "m.emote")]
|
||||
"org.matrix.msc1767.emote" => super::emote,
|
||||
#[cfg(feature = "unstable-msc3956")]
|
||||
#[ruma_enum(alias = "m.encrypted")]
|
||||
"org.matrix.msc1767.encrypted" => super::encrypted,
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
"m.file" => super::file,
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
@ -61,9 +65,8 @@ event_enum! {
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
"m.location" => super::location,
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
"m.message" => super::message,
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
"m.notice" => super::notice,
|
||||
#[ruma_enum(alias = "m.message")]
|
||||
"org.matrix.msc1767.message" => super::message,
|
||||
#[cfg(feature = "unstable-msc3381")]
|
||||
#[ruma_enum(alias = "m.poll.start")]
|
||||
"org.matrix.msc3381.poll.start" => super::poll::start,
|
||||
@ -317,10 +320,10 @@ impl AnyMessageLikeEventContent {
|
||||
Self::RoomMessage(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
Self::Message(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
Self::Notice(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[cfg(feature = "unstable-msc3954")]
|
||||
Self::Emote(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc3956")]
|
||||
Self::Encrypted(ev) => ev.relates_to.clone(),
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
Self::Voice(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
|
@ -9,15 +9,15 @@ use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
room::{message::Relation, EncryptedFile, JsonWebKey},
|
||||
};
|
||||
use crate::{serde::Base64, OwnedMxcUri};
|
||||
|
||||
/// The payload for an extensible file message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3551] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3551] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC3551]: https://github.com/matrix-org/matrix-spec-proposals/pull/3551
|
||||
/// [`message`]: super::message
|
||||
@ -26,8 +26,8 @@ use crate::{serde::Base64, OwnedMxcUri};
|
||||
#[ruma_event(type = "m.file", kind = MessageLike, without_relation)]
|
||||
pub struct FileEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "m.file")]
|
||||
@ -43,55 +43,55 @@ pub struct FileEventContent {
|
||||
}
|
||||
|
||||
impl FileEventContent {
|
||||
/// Creates a new non-encrypted `FileEventContent` with the given plain text message, url and
|
||||
/// file info.
|
||||
/// Creates a new non-encrypted `FileEventContent` with the given fallback representation, url
|
||||
/// and file info.
|
||||
pub fn plain(
|
||||
message: impl Into<String>,
|
||||
text: TextContentBlock,
|
||||
url: OwnedMxcUri,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self { text, file: FileContent::plain(url, info), relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new non-encrypted `FileEventContent` with the given plain text fallback
|
||||
/// representation, url and file info.
|
||||
pub fn plain_with_text(
|
||||
text: impl Into<String>,
|
||||
url: OwnedMxcUri,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text: TextContentBlock::plain(text),
|
||||
file: FileContent::plain(url, info),
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new non-encrypted `FileEventContent` with the given message, url and
|
||||
/// file info.
|
||||
pub fn plain_message(
|
||||
message: MessageContent,
|
||||
url: OwnedMxcUri,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self { message, file: FileContent::plain(url, info), relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `FileEventContent` with the given plain text message, url,
|
||||
/// Creates a new encrypted `FileEventContent` with the given fallback representation, url,
|
||||
/// encryption info and file info.
|
||||
pub fn encrypted(
|
||||
message: impl Into<String>,
|
||||
text: TextContentBlock,
|
||||
url: OwnedMxcUri,
|
||||
encryption_info: EncryptedContent,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self { text, file: FileContent::encrypted(url, encryption_info, info), relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `FileEventContent` with the given plain text fallback
|
||||
/// representation, url, encryption info and file info.
|
||||
pub fn encrypted_with_text(
|
||||
text: impl Into<String>,
|
||||
url: OwnedMxcUri,
|
||||
encryption_info: EncryptedContent,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text: TextContentBlock::plain(text),
|
||||
file: FileContent::encrypted(url, encryption_info, info),
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `FileEventContent` with the given message, url,
|
||||
/// encryption info and file info.
|
||||
pub fn encrypted_message(
|
||||
message: MessageContent,
|
||||
url: OwnedMxcUri,
|
||||
encryption_info: EncryptedContent,
|
||||
info: Option<Box<FileContentInfo>>,
|
||||
) -> Self {
|
||||
Self { message, file: FileContent::encrypted(url, encryption_info, info), relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// File content.
|
||||
|
@ -8,15 +8,15 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
file::{EncryptedContent, FileContent},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
room::message::Relation,
|
||||
};
|
||||
use crate::OwnedMxcUri;
|
||||
|
||||
/// The payload for an extensible image message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3552] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3552] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552
|
||||
/// [`message`]: super::message
|
||||
@ -25,8 +25,8 @@ use crate::OwnedMxcUri;
|
||||
#[ruma_event(type = "m.image", kind = MessageLike, without_relation)]
|
||||
pub struct ImageEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "m.file")]
|
||||
@ -41,13 +41,8 @@ pub struct ImageEventContent {
|
||||
pub thumbnail: Vec<ThumbnailContent>,
|
||||
|
||||
/// The captions of the message.
|
||||
#[serde(
|
||||
rename = "m.caption",
|
||||
with = "super::message::content_serde::as_vec",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub caption: Option<MessageContent>,
|
||||
#[serde(rename = "m.caption", default, skip_serializing_if = "TextContentBlock::is_empty")]
|
||||
pub caption: TextContentBlock,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(
|
||||
@ -59,10 +54,11 @@ pub struct ImageEventContent {
|
||||
}
|
||||
|
||||
impl ImageEventContent {
|
||||
/// Creates a new `ImageEventContent` with the given plain text message and file.
|
||||
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||
/// Creates a new `ImageEventContent` with the given fallback representation and
|
||||
/// file.
|
||||
pub fn new(text: TextContentBlock, file: FileContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text,
|
||||
file,
|
||||
image: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
@ -71,10 +67,11 @@ impl ImageEventContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new non-encrypted `ImageEventContent` with the given message and file.
|
||||
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||
/// Creates a new `ImageEventContent` with the given plain text fallback representation and
|
||||
/// file.
|
||||
pub fn plain(text_fallback: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message,
|
||||
text: TextContentBlock::plain(text_fallback),
|
||||
file,
|
||||
image: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
|
@ -8,13 +8,14 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
mod zoomlevel_serde;
|
||||
|
||||
use super::{message::MessageContent, room::message::Relation};
|
||||
use super::{message::TextContentBlock, room::message::Relation};
|
||||
use crate::{MilliSecondsSinceUnixEpoch, PrivOwnedStr};
|
||||
|
||||
/// The payload for an extensible location message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3488] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3488] and can be sent in rooms with a version
|
||||
/// that doesn't support extensible events. See the documentation of the [`message`] module for more
|
||||
/// information.
|
||||
///
|
||||
/// [MSC3488]: https://github.com/matrix-org/matrix-spec-proposals/pull/3488
|
||||
/// [`message`]: super::message
|
||||
@ -23,8 +24,8 @@ use crate::{MilliSecondsSinceUnixEpoch, PrivOwnedStr};
|
||||
#[ruma_event(type = "m.location", kind = MessageLike, without_relation)]
|
||||
pub struct LocationEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The location info of the message.
|
||||
#[serde(rename = "m.location")]
|
||||
@ -48,21 +49,22 @@ pub struct LocationEventContent {
|
||||
}
|
||||
|
||||
impl LocationEventContent {
|
||||
/// Creates a new `LocationEventContent` with the given plain text representation and location.
|
||||
pub fn plain(message: impl Into<String>, location: LocationContent) -> Self {
|
||||
/// Creates a new `LocationEventContent` with the given fallback representation and location.
|
||||
pub fn new(text: TextContentBlock, location: LocationContent) -> Self {
|
||||
Self { text, location, asset: Default::default(), ts: None, relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new `LocationEventContent` with the given plain text fallback representation and
|
||||
/// location.
|
||||
pub fn plain(text: impl Into<String>, location: LocationContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text: TextContentBlock::plain(text),
|
||||
location,
|
||||
asset: Default::default(),
|
||||
ts: None,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `LocationEventContent` with the given text representation and location.
|
||||
pub fn with_message(message: MessageContent, location: LocationContent) -> Self {
|
||||
Self { message, location, asset: Default::default(), ts: None, relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Location content.
|
||||
|
@ -2,74 +2,116 @@
|
||||
//!
|
||||
//! # Extensible events
|
||||
//!
|
||||
//! MSCs [1767] (Text, Emote and Notice), [3551] (Files), [3552] (Images and Stickers), [3553]
|
||||
//! (Videos), [3246] (Audio), and [3488] (Location) introduce new primary types called extensible
|
||||
//! events. These types are meant to replace the `m.room.message` primary type and its `msgtype`s.
|
||||
//! Other MSCs introduce new types with an `m.room.message` fallback, like [MSC3245] (Voice
|
||||
//! Messages), and types that only have an extensible events format, like [MSC3381] (Polls).
|
||||
//! [MSC1767] defines a new structure for events that is made of two parts: a type and zero or more
|
||||
//! reusable content blocks.
|
||||
//!
|
||||
//! # Transition Period
|
||||
//! This allows to construct new event types from a list of known content blocks that allows in turn
|
||||
//! clients to be able to render unknown event types by using the known content blocks as a
|
||||
//! fallback. When a new type is defined, all the content blocks it can or must contain are defined
|
||||
//! too.
|
||||
//!
|
||||
//! MSC1767 defines a transition period that will start after the extensible events are released in
|
||||
//! a Matrix version. It should last approximately one year, but the end of that period will be
|
||||
//! formalized in a new Matrix version.
|
||||
//! There are also some content blocks called "mixins" that can apply to any event when they are
|
||||
//! defined.
|
||||
//!
|
||||
//! The new primary types should not be sent over the Matrix network before the end of the
|
||||
//! transition period. Instead, transitional `m.room.message` events should be sent. These
|
||||
//! transitional events include the content of the now legacy `m.room.message` event and the content
|
||||
//! of the new extensible event types in a single event.
|
||||
//! # MSCs
|
||||
//!
|
||||
//! # How to use them
|
||||
//! This is a list of MSCs defining the extensible events and deprecating the corresponding legacy
|
||||
//! types. Note that "primary type" means the `type` field at the root of the event and "message
|
||||
//! type" means the `msgtype` field in the content of the `m.room.message` primary type.
|
||||
//!
|
||||
//! - [MSC1767]: Text messages, where the `m.message` primary type replaces the `m.text` message
|
||||
//! type.
|
||||
//! - [MSC3954]: Emotes, where the `m.emote` primary type replaces the `m.emote` message type.
|
||||
//! - [MSC3955]: Automated events, where the `m.automated` mixin replaces the `m.notice` message
|
||||
//! type.
|
||||
//! - [MSC3956]: Encrypted events, where the `m.encrypted` primary type replaces the
|
||||
//! `m.room.encrypted` primary type.
|
||||
//! - [MSC3551]: Files, where the `m.file` primary type replaces the `m.file` message type.
|
||||
//! - [MSC3552]: Images and Stickers, where the `m.image` primary type replaces the `m.image`
|
||||
//! message type and the `m.sticker` primary type.
|
||||
//! - [MSC3553]: Videos, where the `m.video` primary type replaces the `m.video` message type.
|
||||
//! - [MSC3927]: Audio, where the `m.audio` primary type replaces the `m.audio` message type.
|
||||
//! - [MSC3488]: Location, where the `m.location` primary type replaces the `m.location` message
|
||||
//! type.
|
||||
//!
|
||||
//! There are also the following MSCs that introduce new features with extensible events:
|
||||
//!
|
||||
//! - [MSC3245]: Voice Messages.
|
||||
//! - [MSC3246]: Audio Waveform.
|
||||
//! - [MSC3381]: Polls.
|
||||
//!
|
||||
//! # How to use them in Matrix
|
||||
//!
|
||||
//! The extensible events types are meant to be used separately than the legacy types. As such,
|
||||
//! their use is reserved for room versions that support it.
|
||||
//!
|
||||
//! Currently no stable room version supports extensible events so they can only be sent with
|
||||
//! unstable room versions that support them.
|
||||
//!
|
||||
//! An exception is made for some new extensible events types that don't have a legacy type. They
|
||||
//! can be used with stable room versions without support for extensible types, but they might be
|
||||
//! ignored by clients that have no support for extensible events. The types that support this must
|
||||
//! advertise it in their MSC.
|
||||
//!
|
||||
//! Note that if a room version supports extensible events, it doesn't support the legacy types
|
||||
//! anymore and those should be ignored. There is not yet a definition of the deprecated legacy
|
||||
//! types in extensible events rooms.
|
||||
//!
|
||||
//! # How to use them in Ruma
|
||||
//!
|
||||
//! First, you can enable the `unstable-extensible-events` feature from the `ruma` crate, that
|
||||
//! will enable all the MSCs for the extensible events that correspond to the legacy `msgtype`s
|
||||
//! (1767, 3246, 3488, 3551, 3552, 3553). It is also possible to enable only the MSCs you want with
|
||||
//! the `unstable-mscXXXX` features (where `XXXX` is the number of the MSC).
|
||||
//! will enable all the MSCs for the extensible events that correspond to the legacy types. It
|
||||
//! is also possible to enable only the MSCs you want with the `unstable-mscXXXX` features (where
|
||||
//! `XXXX` is the number of the MSC). When enabling an MSC, all MSC dependencies are enabled at the
|
||||
//! same time to avoid issues.
|
||||
//!
|
||||
//! The recommended way to send transitional extensible events while they are unstable and during
|
||||
//! the transition period is to use the constructors and helper methods of
|
||||
//! [`RoomMessageEventContent`]. The data will be duplicated in both the legacy and extensible
|
||||
//! events fields as needed.
|
||||
//! Currently the extensible events use the unstable prefixes as defined in the corresponding MSCs.
|
||||
//!
|
||||
//! [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
//! [1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
//! [3551]: https://github.com/matrix-org/matrix-spec-proposals/pull/3551
|
||||
//! [3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552
|
||||
//! [3553]: https://github.com/matrix-org/matrix-spec-proposals/pull/3553
|
||||
//! [3246]: https://github.com/matrix-org/matrix-spec-proposals/pull/3246
|
||||
//! [3488]: https://github.com/matrix-org/matrix-spec-proposals/pull/3488
|
||||
//! [MSC3954]: https://github.com/matrix-org/matrix-spec-proposals/pull/3954
|
||||
//! [MSC3955]: https://github.com/matrix-org/matrix-spec-proposals/pull/3955
|
||||
//! [MSC3956]: https://github.com/matrix-org/matrix-spec-proposals/pull/3956
|
||||
//! [MSC3551]: https://github.com/matrix-org/matrix-spec-proposals/pull/3551
|
||||
//! [MSC3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552
|
||||
//! [MSC3553]: https://github.com/matrix-org/matrix-spec-proposals/pull/3553
|
||||
//! [MSC3927]: https://github.com/matrix-org/matrix-spec-proposals/pull/3927
|
||||
//! [MSC3488]: https://github.com/matrix-org/matrix-spec-proposals/pull/3488
|
||||
//! [MSC3245]: https://github.com/matrix-org/matrix-spec-proposals/pull/3245
|
||||
//! [MSC3246]: https://github.com/matrix-org/matrix-spec-proposals/pull/3246
|
||||
//! [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
|
||||
//! [`RoomMessageEventContent`]: super::room::message::RoomMessageEventContent
|
||||
use std::ops::Deref;
|
||||
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) mod content_serde;
|
||||
|
||||
use content_serde::MessageContentSerDeHelper;
|
||||
|
||||
use super::room::message::Relation;
|
||||
|
||||
/// The payload for an extensible text message.
|
||||
///
|
||||
/// 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.
|
||||
/// This is the new primary type introduced in [MSC1767] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// To construct a `MessageEventContent` with a custom [`MessageContent`], convert it with
|
||||
/// To construct a `MessageEventContent` with a custom [`TextContentBlock`], convert it with
|
||||
/// `MessageEventContent::from()` / `.into()`.
|
||||
///
|
||||
/// [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
/// [`message`]: super::message
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.message", kind = MessageLike, without_relation)]
|
||||
#[ruma_event(type = "org.matrix.msc1767.message", kind = MessageLike, without_relation)]
|
||||
pub struct MessageEventContent {
|
||||
/// The message's text content.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// Whether this message is automated.
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "crate::serde::is_default",
|
||||
rename = "org.matrix.msc1767.automated"
|
||||
)]
|
||||
pub automated: bool,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(
|
||||
@ -83,77 +125,89 @@ pub struct MessageEventContent {
|
||||
impl MessageEventContent {
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::plain(body), relates_to: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML message.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::html(body, html_body), relates_to: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a Markdown message.
|
||||
///
|
||||
/// Returns an HTML message if some Markdown formatting was detected, otherwise returns a plain
|
||||
/// text message.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
Self { message: MessageContent::markdown(body), relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageContent> for MessageEventContent {
|
||||
fn from(message: MessageContent) -> Self {
|
||||
Self { message, relates_to: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Text message content.
|
||||
///
|
||||
/// A `MessageContent` must contain at least one message to be used as a fallback text
|
||||
/// 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)]
|
||||
#[serde(try_from = "MessageContentSerDeHelper")]
|
||||
pub struct MessageContent(pub(crate) Vec<Text>);
|
||||
|
||||
impl MessageContent {
|
||||
/// Create a `MessageContent` from an array of messages.
|
||||
///
|
||||
/// Returns `None` if the array is empty.
|
||||
pub fn new(messages: Vec<Text>) -> Option<Self> {
|
||||
if messages.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Self(messages))
|
||||
Self {
|
||||
text: TextContentBlock::plain(body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self(vec![Text::plain(body)])
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML message.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self(vec![Text::html(html_body), Text::plain(body)])
|
||||
Self {
|
||||
text: TextContentBlock::html(body, html_body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a Markdown message.
|
||||
/// A convenience constructor to create a message from Markdown.
|
||||
///
|
||||
/// Returns an HTML message if some Markdown formatting was detected, otherwise returns a plain
|
||||
/// text message.
|
||||
/// The content includes an HTML message if some Markdown formatting was detected, otherwise
|
||||
/// only a plain text message is included.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
Self {
|
||||
text: TextContentBlock::markdown(body),
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextContentBlock> for MessageEventContent {
|
||||
fn from(text: TextContentBlock) -> Self {
|
||||
Self {
|
||||
text,
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A block for text content with optional markup.
|
||||
///
|
||||
/// This is an array of [`TextRepresentation`].
|
||||
///
|
||||
/// To construct a `TextContentBlock` with custom MIME types, construct a `Vec<TextRepresentation>`
|
||||
/// first and use its `::from()` / `.into()` implementation.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct TextContentBlock(Vec<TextRepresentation>);
|
||||
|
||||
impl TextContentBlock {
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self(vec![TextRepresentation::plain(body)])
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML message with a plain text fallback.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self(vec![TextRepresentation::html(html_body), TextRepresentation::plain(body)])
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a message from Markdown.
|
||||
///
|
||||
/// The content includes an HTML message if some Markdown formatting was detected, otherwise
|
||||
/// only a plain text message is included.
|
||||
#[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) {
|
||||
if let Some(html_body) = TextRepresentation::markdown(&body) {
|
||||
message.push(html_body);
|
||||
}
|
||||
message.push(Text::plain(body));
|
||||
message.push(TextRepresentation::plain(body));
|
||||
Self(message)
|
||||
}
|
||||
|
||||
/// Whether this content block is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Get the plain text representation of this message.
|
||||
pub fn find_plain(&self) -> Option<&str> {
|
||||
self.iter()
|
||||
@ -169,38 +223,39 @@ impl MessageContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// The error type returned when trying to construct an empty `MessageContent`.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
#[error("MessageContent cannot be empty")]
|
||||
pub struct EmptyMessageContentError;
|
||||
|
||||
impl TryFrom<Vec<Text>> for MessageContent {
|
||||
type Error = EmptyMessageContentError;
|
||||
|
||||
fn try_from(messages: Vec<Text>) -> Result<Self, Self::Error> {
|
||||
Self::new(messages).ok_or(EmptyMessageContentError)
|
||||
impl From<Vec<TextRepresentation>> for TextContentBlock {
|
||||
fn from(representations: Vec<TextRepresentation>) -> Self {
|
||||
Self(representations)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MessageContent {
|
||||
type Target = [Text];
|
||||
impl FromIterator<TextRepresentation> for TextContentBlock {
|
||||
fn from_iter<T: IntoIterator<Item = TextRepresentation>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TextContentBlock {
|
||||
type Target = [TextRepresentation];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Text message content.
|
||||
/// Text content with optional markup.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct Text {
|
||||
pub struct TextRepresentation {
|
||||
/// 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 = "TextRepresentation::default_mimetype",
|
||||
skip_serializing_if = "TextRepresentation::is_default_mimetype"
|
||||
)]
|
||||
pub mimetype: String,
|
||||
|
||||
/// The text content.
|
||||
@ -216,8 +271,8 @@ pub struct Text {
|
||||
pub lang: Option<String>,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
/// Creates a new `Text` with the given MIME type and body.
|
||||
impl TextRepresentation {
|
||||
/// Creates a new `TextRepresentation` with the given MIME type and body.
|
||||
pub fn new(mimetype: impl Into<String>, body: impl Into<String>) -> Self {
|
||||
Self {
|
||||
mimetype: mimetype.into(),
|
||||
@ -250,13 +305,8 @@ impl Text {
|
||||
fn default_mimetype() -> String {
|
||||
"text/plain".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error type returned when a conversion to an extensible event type fails.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TryFromExtensibleError {
|
||||
/// A field is missing.
|
||||
#[error("missing field `{0}`")]
|
||||
MissingField(String),
|
||||
fn is_default_mimetype(mime: &str) -> bool {
|
||||
mime == "text/plain"
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +0,0 @@
|
||||
//! `Serialize` and `Deserialize` implementations for extensible events (MSC1767).
|
||||
|
||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||
|
||||
use super::{MessageContent, Text, TryFromExtensibleError};
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(crate) struct MessageContentSerDeHelper {
|
||||
/// Plain text short form, stable name.
|
||||
#[serde(rename = "m.text")]
|
||||
text_stable: Option<String>,
|
||||
|
||||
/// Plain text short form, unstable name.
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
text_unstable: Option<String>,
|
||||
|
||||
/// HTML short form, stable name.
|
||||
#[serde(rename = "m.html")]
|
||||
html_stable: Option<String>,
|
||||
|
||||
/// HTML short form, unstable name.
|
||||
#[serde(rename = "org.matrix.msc1767.html")]
|
||||
html_unstable: Option<String>,
|
||||
|
||||
/// Long form, stable name.
|
||||
#[serde(rename = "m.message")]
|
||||
message_stable: Option<Vec<Text>>,
|
||||
|
||||
/// Long form, unstable name.
|
||||
#[serde(rename = "org.matrix.msc1767.message")]
|
||||
message_unstable: Option<Vec<Text>>,
|
||||
}
|
||||
|
||||
impl TryFrom<MessageContentSerDeHelper> for MessageContent {
|
||||
type Error = TryFromExtensibleError;
|
||||
|
||||
fn try_from(helper: MessageContentSerDeHelper) -> Result<Self, Self::Error> {
|
||||
let MessageContentSerDeHelper {
|
||||
text_stable,
|
||||
text_unstable,
|
||||
html_stable,
|
||||
html_unstable,
|
||||
message_stable,
|
||||
message_unstable,
|
||||
} = helper;
|
||||
|
||||
if let Some(message) = message_stable.or(message_unstable) {
|
||||
Ok(Self(message))
|
||||
} else {
|
||||
let message: Vec<_> = html_stable
|
||||
.or(html_unstable)
|
||||
.map(Text::html)
|
||||
.into_iter()
|
||||
.chain(text_stable.or(text_unstable).map(Text::plain))
|
||||
.collect();
|
||||
if !message.is_empty() {
|
||||
Ok(Self(message))
|
||||
} else {
|
||||
Err(TryFromExtensibleError::MissingField("m.message, m.text or m.html".to_owned()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for MessageContent {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
#[cfg(feature = "unstable-msc3554")]
|
||||
let has_shortcut = |message: &Text| {
|
||||
matches!(&*message.mimetype, "text/plain" | "text/html") && message.lang.is_none()
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "unstable-msc3554"))]
|
||||
let has_shortcut =
|
||||
|message: &Text| matches!(&*message.mimetype, "text/plain" | "text/html");
|
||||
|
||||
if self.iter().all(has_shortcut) {
|
||||
let mut st = serializer.serialize_struct("MessageContent", self.len())?;
|
||||
for message in self.iter() {
|
||||
if message.mimetype == "text/plain" {
|
||||
st.serialize_field("org.matrix.msc1767.text", &message.body)?;
|
||||
} else if message.mimetype == "text/html" {
|
||||
st.serialize_field("org.matrix.msc1767.html", &message.body)?;
|
||||
}
|
||||
}
|
||||
st.end()
|
||||
} else {
|
||||
let mut st = serializer.serialize_struct("MessageContent", 1)?;
|
||||
st.serialize_field("org.matrix.msc1767.message", &self.0)?;
|
||||
st.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod as_vec {
|
||||
use serde::{de, 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).and_then(|content| {
|
||||
content.map(MessageContent::new).ok_or_else(|| {
|
||||
de::Error::invalid_value(de::Unexpected::Other("empty array"), &"a non-empty array")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
//! Types for extensible notice message events ([MSC1767]).
|
||||
//!
|
||||
//! [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{message::MessageContent, room::message::Relation};
|
||||
|
||||
/// The payload for an extensible notice message.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// [MSC1767]: https://github.com/matrix-org/matrix-spec-proposals/pull/1767
|
||||
/// [`message`]: super::message
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.notice", kind = MessageLike, without_relation)]
|
||||
pub struct NoticeEventContent {
|
||||
/// The message's text content.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(
|
||||
flatten,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "crate::events::room::message::relation_serde::deserialize_relation"
|
||||
)]
|
||||
pub relates_to: Option<Relation<NoticeEventContentWithoutRelation>>,
|
||||
}
|
||||
|
||||
impl NoticeEventContent {
|
||||
/// A convenience constructor to create a plain text notice.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::plain(body), relates_to: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML notice.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self { message: MessageContent::html(body, html_body), relates_to: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a Markdown notice.
|
||||
///
|
||||
/// Returns an HTML notice if some Markdown formatting was detected, otherwise returns a plain
|
||||
/// text notice.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
Self { message: MessageContent::markdown(body), relates_to: None }
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ mod poll_answers_serde;
|
||||
|
||||
use poll_answers_serde::PollAnswersDeHelper;
|
||||
|
||||
use crate::{events::message::MessageContent, serde::StringEnum, PrivOwnedStr};
|
||||
use crate::{events::message::TextContentBlock, serde::StringEnum, PrivOwnedStr};
|
||||
|
||||
/// The payload for a poll start event.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
@ -20,14 +20,18 @@ pub struct PollStartEventContent {
|
||||
pub poll_start: PollStartContent,
|
||||
|
||||
/// Optional fallback text representation of the message, for clients that don't support polls.
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.text",
|
||||
default,
|
||||
skip_serializing_if = "TextContentBlock::is_empty"
|
||||
)]
|
||||
pub text: TextContentBlock,
|
||||
}
|
||||
|
||||
impl PollStartEventContent {
|
||||
/// Creates a new `PollStartEventContent` with the given poll start content.
|
||||
pub fn new(poll_start: PollStartContent) -> Self {
|
||||
Self { poll_start, message: None }
|
||||
Self { poll_start, text: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +40,7 @@ impl PollStartEventContent {
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct PollStartContent {
|
||||
/// The question of the poll.
|
||||
pub question: MessageContent,
|
||||
pub question: PollQuestion,
|
||||
|
||||
/// The kind of the poll.
|
||||
#[serde(default)]
|
||||
@ -59,8 +63,13 @@ pub struct PollStartContent {
|
||||
|
||||
impl PollStartContent {
|
||||
/// Creates a new `PollStartContent` with the given question, kind, and answers.
|
||||
pub fn new(question: MessageContent, kind: PollKind, answers: PollAnswers) -> Self {
|
||||
Self { question, kind, max_selections: Self::default_max_selections(), answers }
|
||||
pub fn new(question: TextContentBlock, kind: PollKind, answers: PollAnswers) -> Self {
|
||||
Self {
|
||||
question: question.into(),
|
||||
kind,
|
||||
max_selections: Self::default_max_selections(),
|
||||
answers,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_max_selections() -> UInt {
|
||||
@ -72,6 +81,21 @@ impl PollStartContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// The question of a poll.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct PollQuestion {
|
||||
/// The text representation of the question.
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
}
|
||||
|
||||
impl From<TextContentBlock> for PollQuestion {
|
||||
fn from(text: TextContentBlock) -> Self {
|
||||
Self { text }
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of poll.
|
||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
|
||||
#[derive(Clone, Default, PartialEq, Eq, StringEnum)]
|
||||
@ -156,13 +180,13 @@ pub struct PollAnswer {
|
||||
pub id: String,
|
||||
|
||||
/// The text representation of the answer.
|
||||
#[serde(flatten)]
|
||||
pub answer: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
}
|
||||
|
||||
impl PollAnswer {
|
||||
/// Creates a new `PollAnswer` with the given id and text representation.
|
||||
pub fn new(id: String, answer: MessageContent) -> Self {
|
||||
Self { id, answer }
|
||||
pub fn new(id: String, text: TextContentBlock) -> Self {
|
||||
Self { id, text }
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
OwnedDeviceId, OwnedEventId,
|
||||
};
|
||||
|
||||
mod relation_serde;
|
||||
pub(crate) mod relation_serde;
|
||||
|
||||
/// The content of an `m.room.encrypted` event.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
|
@ -5,7 +5,7 @@ use super::Annotation;
|
||||
use super::{InReplyTo, Reference, Relation, Replacement, Thread};
|
||||
use crate::OwnedEventId;
|
||||
|
||||
pub(super) fn deserialize_relation<'de, D>(deserializer: D) -> Result<Option<Relation>, D::Error>
|
||||
pub(crate) fn deserialize_relation<'de, D>(deserializer: D) -> Result<Option<Relation>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
|
@ -9,13 +9,13 @@ use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
file::FileContent, image::ThumbnailContent, message::MessageContent, room::message::Relation,
|
||||
file::FileContent, image::ThumbnailContent, message::TextContentBlock, room::message::Relation,
|
||||
};
|
||||
|
||||
/// The payload for an extensible video message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3553] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3553] and should only be sent in rooms with a
|
||||
/// version that supports it. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [MSC3553]: https://github.com/matrix-org/matrix-spec-proposals/pull/3553
|
||||
/// [`message`]: super::message
|
||||
@ -24,8 +24,8 @@ use super::{
|
||||
#[ruma_event(type = "m.video", kind = MessageLike, without_relation)]
|
||||
pub struct VideoEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "m.file")]
|
||||
@ -40,13 +40,8 @@ pub struct VideoEventContent {
|
||||
pub thumbnail: Vec<ThumbnailContent>,
|
||||
|
||||
/// The captions of the message.
|
||||
#[serde(
|
||||
rename = "m.caption",
|
||||
with = "super::message::content_serde::as_vec",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub caption: Option<MessageContent>,
|
||||
#[serde(rename = "m.caption", default, skip_serializing_if = "TextContentBlock::is_empty")]
|
||||
pub caption: TextContentBlock,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(
|
||||
@ -58,10 +53,10 @@ pub struct VideoEventContent {
|
||||
}
|
||||
|
||||
impl VideoEventContent {
|
||||
/// Creates a new `VideoEventContent` with the given plain text message and file.
|
||||
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||
/// Creates a new `VideoEventContent` with the given fallback representation and file.
|
||||
pub fn new(text: TextContentBlock, file: FileContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
text,
|
||||
file,
|
||||
video: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
@ -70,10 +65,11 @@ impl VideoEventContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `VideoEventContent` with the given message and file.
|
||||
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||
/// Creates a new `VideoEventContent` with the given plain text fallback representation and
|
||||
/// file.
|
||||
pub fn plain(text: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message,
|
||||
text: TextContentBlock::plain(text),
|
||||
file,
|
||||
video: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
|
@ -6,13 +6,14 @@ use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
audio::AudioContent, file::FileContent, message::MessageContent, room::message::Relation,
|
||||
audio::AudioContent, file::FileContent, message::TextContentBlock, room::message::Relation,
|
||||
};
|
||||
|
||||
/// The payload for an extensible voice message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC3245] and should not be sent before the end of
|
||||
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||
/// This is the new primary type introduced in [MSC3245] and can be sent in rooms with a version
|
||||
/// that doesn't support extensible events. See the documentation of the [`message`] module for more
|
||||
/// information.
|
||||
///
|
||||
/// [MSC3245]: https://github.com/matrix-org/matrix-spec-proposals/pull/3245
|
||||
/// [`message`]: super::message
|
||||
@ -21,8 +22,8 @@ use super::{
|
||||
#[ruma_event(type = "m.voice", kind = MessageLike, without_relation)]
|
||||
pub struct VoiceEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
pub text: TextContentBlock,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "m.file")]
|
||||
@ -46,21 +47,16 @@ pub struct VoiceEventContent {
|
||||
}
|
||||
|
||||
impl VoiceEventContent {
|
||||
/// Creates a new `VoiceEventContent` with the given plain text representation and file.
|
||||
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
file,
|
||||
audio: Default::default(),
|
||||
voice: Default::default(),
|
||||
relates_to: None,
|
||||
}
|
||||
/// Creates a new `VoiceEventContent` with the given fallback representation and file.
|
||||
pub fn new(text: TextContentBlock, file: FileContent) -> Self {
|
||||
Self { text, file, audio: Default::default(), voice: Default::default(), relates_to: None }
|
||||
}
|
||||
|
||||
/// Creates a new `VoiceEventContent` with the given message and file.
|
||||
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||
/// Creates a new `VoiceEventContent` with the given plain text fallback representation and
|
||||
/// file.
|
||||
pub fn plain(text: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message,
|
||||
text: TextContentBlock::plain(text),
|
||||
file,
|
||||
audio: Default::default(),
|
||||
voice: Default::default(),
|
||||
|
@ -10,7 +10,7 @@ use ruma_common::{
|
||||
events::{
|
||||
audio::{Amplitude, AudioContent, AudioEventContent, Waveform, WaveformError},
|
||||
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::{message::Relation, JsonWebKeyInit},
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
@ -63,7 +63,9 @@ fn plain_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_sound.ogg",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_sound.ogg" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -103,7 +105,9 @@ fn encrypted_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_sound.ogg",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_sound.ogg" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -127,8 +131,8 @@ fn encrypted_content_serialization() {
|
||||
#[test]
|
||||
fn event_serialization() {
|
||||
let content = assign!(
|
||||
AudioEventContent::with_message(
|
||||
MessageContent::html(
|
||||
AudioEventContent::new(
|
||||
TextContentBlock::html(
|
||||
"Upload: my_mix.mp3",
|
||||
"Upload: <strong>my_mix.mp3</strong>",
|
||||
),
|
||||
@ -160,8 +164,10 @@ fn event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Upload: <strong>my_mix.mp3</strong>",
|
||||
"org.matrix.msc1767.text": "Upload: my_mix.mp3",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Upload: <strong>my_mix.mp3</strong>" },
|
||||
{ "body": "Upload: my_mix.mp3"},
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_mix.mp3",
|
||||
@ -183,7 +189,9 @@ fn event_serialization() {
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Upload: my_new_song.webm",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_new_song.webm" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -246,8 +254,8 @@ fn plain_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<AudioEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_new_song.webm"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_new_song.webm"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
let waveform = content.audio.waveform.unwrap();
|
||||
assert_eq!(waveform.amplitudes().len(), 52);
|
||||
@ -256,7 +264,9 @@ fn plain_content_deserialization() {
|
||||
#[test]
|
||||
fn encrypted_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -276,8 +286,8 @@ fn encrypted_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<AudioEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert!(content.file.encryption_info.is_some());
|
||||
}
|
||||
@ -286,7 +296,9 @@ fn encrypted_content_deserialization() {
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.text": "Upload: airplane_sound.opus",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: airplane_sound.opus" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "airplane_sound.opus",
|
||||
@ -316,8 +328,8 @@ fn message_event_deserialization() {
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
|
||||
let content = message_event.content;
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: airplane_sound.opus"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: airplane_sound.opus"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
let info = content.file.info.unwrap();
|
||||
assert_eq!(info.name.as_deref(), Some("airplane_sound.opus"));
|
||||
|
@ -7,7 +7,7 @@ use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
file::{EncryptedContentInit, FileContentInfo, FileEventContent},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::{message::Relation, JsonWebKeyInit},
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
@ -20,7 +20,7 @@ use serde_json::{from_value as from_json_value, json, to_value as to_json_value}
|
||||
|
||||
#[test]
|
||||
fn plain_content_serialization() {
|
||||
let event_content = FileEventContent::plain(
|
||||
let event_content = FileEventContent::plain_with_text(
|
||||
"Upload: my_file.txt",
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
None,
|
||||
@ -29,7 +29,9 @@ fn plain_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
}
|
||||
@ -39,7 +41,7 @@ fn plain_content_serialization() {
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_serialization() {
|
||||
let event_content = FileEventContent::encrypted(
|
||||
let event_content = FileEventContent::encrypted_with_text(
|
||||
"Upload: my_file.txt",
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
EncryptedContentInit {
|
||||
@ -66,7 +68,9 @@ fn encrypted_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -89,8 +93,8 @@ fn encrypted_content_serialization() {
|
||||
#[test]
|
||||
fn file_event_serialization() {
|
||||
let content = assign!(
|
||||
FileEventContent::plain_message(
|
||||
MessageContent::html(
|
||||
FileEventContent::plain(
|
||||
TextContentBlock::html(
|
||||
"Upload: my_file.txt",
|
||||
"Upload: <strong>my_file.txt</strong>",
|
||||
),
|
||||
@ -114,8 +118,10 @@ fn file_event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Upload: <strong>my_file.txt</strong>",
|
||||
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Upload: <strong>my_file.txt</strong>" },
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_file.txt",
|
||||
@ -134,22 +140,26 @@ fn file_event_serialization() {
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
}
|
||||
});
|
||||
|
||||
let content = from_json_value::<FileEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Upload: my_file.txt",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_file.txt" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -168,8 +178,8 @@ fn encrypted_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<FileEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert!(content.file.encryption_info.is_some());
|
||||
}
|
||||
@ -178,7 +188,7 @@ fn encrypted_content_deserialization() {
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.message": [
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: <strong>my_file.txt</strong>", "mimetype": "text/html"},
|
||||
{ "body": "Upload: my_file.txt", "mimetype": "text/plain"},
|
||||
],
|
||||
@ -202,8 +212,8 @@ fn message_event_deserialization() {
|
||||
);
|
||||
assert_eq!(message_event.event_id, "$event:notareal.hs");
|
||||
let content = message_event.content;
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.message.find_html(), Some("Upload: <strong>my_file.txt</strong>"));
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_file.txt"));
|
||||
assert_eq!(content.text.find_html(), Some("Upload: <strong>my_file.txt</strong>"));
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
let info = content.file.info.unwrap();
|
||||
assert_eq!(info.name.as_deref(), Some("my_file.txt"));
|
||||
|
@ -11,7 +11,7 @@ use ruma_common::{
|
||||
ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent,
|
||||
ThumbnailFileContentInfo,
|
||||
},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::{message::Relation, JsonWebKeyInit},
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
@ -32,7 +32,9 @@ fn plain_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_image.jpg",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_image.jpg" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -72,7 +74,9 @@ fn encrypted_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_image.jpg",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_image.jpg" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -96,8 +100,8 @@ fn encrypted_content_serialization() {
|
||||
#[test]
|
||||
fn image_event_serialization() {
|
||||
let content = assign!(
|
||||
ImageEventContent::with_message(
|
||||
MessageContent::html(
|
||||
ImageEventContent::new(
|
||||
TextContentBlock::html(
|
||||
"Upload: my_house.jpg",
|
||||
"Upload: <strong>my_house.jpg</strong>",
|
||||
),
|
||||
@ -125,7 +129,7 @@ fn image_event_serialization() {
|
||||
),
|
||||
None
|
||||
)],
|
||||
caption: Some(MessageContent::plain("This is my house")),
|
||||
caption: TextContentBlock::plain("This is my house"),
|
||||
relates_to: Some(Relation::Reply {
|
||||
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||
}),
|
||||
@ -135,8 +139,10 @@ fn image_event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Upload: <strong>my_house.jpg</strong>",
|
||||
"org.matrix.msc1767.text": "Upload: my_house.jpg",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Upload: <strong>my_house.jpg</strong>" },
|
||||
{ "body": "Upload: my_house.jpg" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_house.jpg",
|
||||
@ -157,7 +163,6 @@ fn image_event_serialization() {
|
||||
"m.caption": [
|
||||
{
|
||||
"body": "This is my house",
|
||||
"mimetype": "text/plain",
|
||||
}
|
||||
],
|
||||
"m.relates_to": {
|
||||
@ -172,7 +177,9 @@ fn image_event_serialization() {
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Upload: my_cat.png",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_cat.png" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -187,21 +194,23 @@ fn plain_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<ImageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_cat.png"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_cat.png"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_matches!(content.file.encryption_info, None);
|
||||
assert_eq!(content.image.width, Some(uint!(668)));
|
||||
assert_eq!(content.image.height, None);
|
||||
assert_eq!(content.thumbnail.len(), 0);
|
||||
let caption = content.caption.unwrap();
|
||||
assert_eq!(caption.find_plain(), Some("Look at my cat!"));
|
||||
assert_eq!(content.caption.len(), 1);
|
||||
assert_eq!(content.caption.find_plain(), Some("Look at my cat!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_cat.png",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_cat.png" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -226,22 +235,24 @@ fn encrypted_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<ImageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_cat.png"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_cat.png"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert!(content.file.encryption_info.is_some());
|
||||
assert_eq!(content.image.width, None);
|
||||
assert_eq!(content.image.height, None);
|
||||
assert_eq!(content.thumbnail.len(), 1);
|
||||
assert_eq!(content.thumbnail[0].file.url, "mxc://notareal.hs/thumbnail");
|
||||
assert_matches!(content.caption, None);
|
||||
assert!(content.caption.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.text": "Upload: my_gnome.webp",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_gnome.webp" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_gnome.webp",
|
||||
@ -271,8 +282,8 @@ fn message_event_deserialization() {
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
let content = message_event.content;
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_gnome.webp"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_gnome.webp"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
let info = content.file.info.unwrap();
|
||||
assert_eq!(info.name.as_deref(), Some("my_gnome.webp"));
|
||||
|
@ -7,7 +7,7 @@ use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
location::{AssetType, LocationContent, LocationEventContent, ZoomLevel, ZoomLevelError},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::message::Relation,
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
@ -28,7 +28,9 @@ fn plain_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Alice was at geo:51.5008,0.1247;u=35" },
|
||||
],
|
||||
"m.location": {
|
||||
"uri": "geo:51.5008,0.1247;u=35",
|
||||
},
|
||||
@ -39,8 +41,8 @@ fn plain_content_serialization() {
|
||||
#[test]
|
||||
fn event_serialization() {
|
||||
let content = assign!(
|
||||
LocationEventContent::with_message(
|
||||
MessageContent::html(
|
||||
LocationEventContent::new(
|
||||
TextContentBlock::html(
|
||||
"Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021",
|
||||
"Alice was at <strong>geo:51.5008,0.1247;u=35</strong> as of <em>Sat Nov 13 18:50:58 2021</em>",
|
||||
),
|
||||
@ -63,8 +65,15 @@ fn event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Alice was at <strong>geo:51.5008,0.1247;u=35</strong> as of <em>Sat Nov 13 18:50:58 2021</em>",
|
||||
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021",
|
||||
"org.matrix.msc1767.text": [
|
||||
{
|
||||
"mimetype": "text/html",
|
||||
"body": "Alice was at <strong>geo:51.5008,0.1247;u=35</strong> as of <em>Sat Nov 13 18:50:58 2021</em>"
|
||||
},
|
||||
{
|
||||
"body": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021"
|
||||
},
|
||||
],
|
||||
"m.location": {
|
||||
"uri": "geo:51.5008,0.1247;u=35",
|
||||
"description": "Alice's whereabouts",
|
||||
@ -83,7 +92,9 @@ fn event_serialization() {
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Alice was at geo:51.5008,0.1247;u=35" },
|
||||
],
|
||||
"m.location": {
|
||||
"uri": "geo:51.5008,0.1247;u=35",
|
||||
},
|
||||
@ -91,8 +102,8 @@ fn plain_content_deserialization() {
|
||||
|
||||
let ev = from_json_value::<LocationEventContent>(json_data).unwrap();
|
||||
|
||||
assert_eq!(ev.message.find_plain(), Some("Alice was at geo:51.5008,0.1247;u=35"));
|
||||
assert_eq!(ev.message.find_html(), None);
|
||||
assert_eq!(ev.text.find_plain(), Some("Alice was at geo:51.5008,0.1247;u=35"));
|
||||
assert_eq!(ev.text.find_html(), None);
|
||||
assert_eq!(ev.location.uri, "geo:51.5008,0.1247;u=35");
|
||||
assert_eq!(ev.location.description, None);
|
||||
assert_matches!(ev.location.zoom_level, None);
|
||||
@ -132,7 +143,7 @@ fn zoomlevel_deserialization_too_high() {
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.message": [
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021" },
|
||||
],
|
||||
"m.location": {
|
||||
@ -160,10 +171,10 @@ fn message_event_deserialization() {
|
||||
assert_matches!(ev, AnyMessageLikeEvent::Location(MessageLikeEvent::Original(ev)) => ev);
|
||||
|
||||
assert_eq!(
|
||||
ev.content.message.find_plain(),
|
||||
ev.content.text.find_plain(),
|
||||
Some("Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021")
|
||||
);
|
||||
assert_eq!(ev.content.message.find_html(), None);
|
||||
assert_eq!(ev.content.text.find_html(), None);
|
||||
assert_eq!(ev.content.location.uri, "geo:51.5008,0.1247;u=35");
|
||||
assert_eq!(ev.content.location.description.as_deref(), Some("Alice's whereabouts"));
|
||||
assert_eq!(ev.content.location.zoom_level.unwrap().get(), uint!(4));
|
||||
|
@ -7,8 +7,7 @@ use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
emote::EmoteEventContent,
|
||||
message::{MessageContent, MessageEventContent, Text},
|
||||
notice::NoticeEventContent,
|
||||
message::{MessageEventContent, TextContentBlock, TextRepresentation},
|
||||
relation::InReplyTo,
|
||||
room::message::Relation,
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
@ -18,17 +17,6 @@ use ruma_common::{
|
||||
};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
#[test]
|
||||
fn try_from_valid() {
|
||||
let message = MessageContent::try_from(vec![Text::plain("A message")]).unwrap();
|
||||
assert_eq!(message.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_from_invalid() {
|
||||
assert_matches!(MessageContent::try_from(vec![]), Err(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn html_content_serialization() {
|
||||
let message_event_content =
|
||||
@ -37,8 +25,10 @@ fn html_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Hello, <em>World</em>!",
|
||||
"org.matrix.msc1767.text": "Hello, World!",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Hello, <em>World</em>!" },
|
||||
{ "body": "Hello, World!" },
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -51,7 +41,9 @@ fn plain_text_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "> <@test:example.com> test\n\ntest reply",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "> <@test:example.com> test\n\ntest reply" },
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -59,9 +51,9 @@ 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(
|
||||
TextContentBlock::try_from(vec![
|
||||
TextRepresentation::plain("> <@test:example.com> test\n\ntest reply"),
|
||||
TextRepresentation::new(
|
||||
"application/json",
|
||||
r#"{ "quote": "<@test:example.com> test", "reply": "test reply" }"#,
|
||||
),
|
||||
@ -72,16 +64,15 @@ fn unknown_mimetype_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.message": [
|
||||
"org.matrix.msc1767.text": [
|
||||
{
|
||||
"body": "> <@test:example.com> test\n\ntest reply",
|
||||
"mimetype": "text/plain",
|
||||
},
|
||||
{
|
||||
"body": r#"{ "quote": "<@test:example.com> test", "reply": "test reply" }"#,
|
||||
"mimetype": "application/json",
|
||||
},
|
||||
]
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -94,8 +85,15 @@ fn markdown_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&formatted_message).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "<p>Testing <strong>bold</strong> and <em>italic</em>!</p>\n",
|
||||
"org.matrix.msc1767.text": "Testing **bold** and _italic_!",
|
||||
"org.matrix.msc1767.text": [
|
||||
{
|
||||
"mimetype": "text/html",
|
||||
"body": "<p>Testing <strong>bold</strong> and <em>italic</em>!</p>\n",
|
||||
},
|
||||
{
|
||||
"body": "Testing **bold** and _italic_!",
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
@ -104,7 +102,9 @@ fn markdown_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&plain_message_simple).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Testing a simple phrase…",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Testing a simple phrase…" },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
@ -114,8 +114,15 @@ fn markdown_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&plain_message_paragraphs).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "<p>Testing</p>\n<p>Several</p>\n<p>Paragraphs.</p>\n",
|
||||
"org.matrix.msc1767.text": "Testing\n\nSeveral\n\nParagraphs.",
|
||||
"org.matrix.msc1767.text": [
|
||||
{
|
||||
"mimetype": "text/html",
|
||||
"body": "<p>Testing</p>\n<p>Several</p>\n<p>Paragraphs.</p>\n",
|
||||
},
|
||||
{
|
||||
"body": "Testing\n\nSeveral\n\nParagraphs.",
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -132,7 +139,9 @@ fn relates_to_content_serialization() {
|
||||
});
|
||||
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "> <@test:example.com> test\n\ntest reply",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "> <@test:example.com> test\n\ntest reply" },
|
||||
],
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$15827405538098VGFWH:example.com"
|
||||
@ -148,110 +157,66 @@ fn message_event_serialization() {
|
||||
let content = MessageEventContent::plain("Hello, World!");
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({ "org.matrix.msc1767.text": "Hello, World!" })
|
||||
json!({
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Hello, World!" },
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_text_content_unstable_deserialization() {
|
||||
fn plain_text_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "This is my body",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "This is my body" },
|
||||
],
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("This is my body"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("This is my body"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
assert!(!content.automated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_text_content_stable_deserialization() {
|
||||
fn html_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "This is my body",
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("This is my body"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn html_content_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.html": "Hello, <em>New World</em>!",
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), None);
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn html_content_stable_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.html": "Hello, <em>New World</em>!",
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), None);
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn html_and_text_content_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.html": "Hello, <em>New World</em>!",
|
||||
"org.matrix.msc1767.text": "Hello, New World!",
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Hello, New World!"));
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn html_and_text_content_stable_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.html": "Hello, <em>New World</em>!",
|
||||
"m.text": "Hello, New World!",
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Hello, New World!"));
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_content_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.message": [
|
||||
{ "body": "Hello, <em>New World</em>!", "mimetype": "text/html"},
|
||||
{ "body": "Hello, New World!" },
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Hello, <em>New World</em>!" },
|
||||
]
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Hello, New World!"));
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
assert_eq!(content.text.find_plain(), None);
|
||||
assert_eq!(content.text.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
assert!(!content.automated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_content_stable_deserialization() {
|
||||
fn html_and_text_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.message": [
|
||||
{ "body": "Hello, <em>New World</em>!", "mimetype": "text/html"},
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Hello, <em>New World</em>!" },
|
||||
{ "body": "Hello, New World!" },
|
||||
]
|
||||
],
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Hello, New World!"));
|
||||
assert_eq!(content.message.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
assert_eq!(content.text.find_plain(), Some("Hello, New World!"));
|
||||
assert_eq!(content.text.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
assert!(!content.automated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relates_to_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "> <@test:example.com> test\n\ntest reply",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "> <@test:example.com> test\n\ntest reply" },
|
||||
],
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$15827405538098VGFWH:example.com"
|
||||
@ -260,8 +225,8 @@ fn relates_to_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("> <@test:example.com> test\n\ntest reply"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("> <@test:example.com> test\n\ntest reply"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
|
||||
let event_id = assert_matches!(
|
||||
content.relates_to,
|
||||
@ -274,13 +239,15 @@ fn relates_to_content_deserialization() {
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.text": "Hello, World!",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Hello, World!" },
|
||||
],
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.message",
|
||||
"type": "org.matrix.msc1767.message",
|
||||
});
|
||||
|
||||
let message_event = assert_matches!(
|
||||
@ -288,7 +255,7 @@ fn message_event_deserialization() {
|
||||
Ok(AnyMessageLikeEvent::Message(MessageLikeEvent::Original(message_event))) => message_event
|
||||
);
|
||||
assert_eq!(message_event.event_id, "$event:notareal.hs");
|
||||
assert_eq!(message_event.content.message.find_plain(), Some("Hello, World!"));
|
||||
assert_eq!(message_event.content.text.find_plain(), Some("Hello, World!"));
|
||||
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(134_829_848)));
|
||||
assert_eq!(message_event.room_id, "!roomid:notareal.hs");
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
@ -296,79 +263,7 @@ fn message_event_deserialization() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_event_serialization() {
|
||||
let content = NoticeEventContent::plain("Hello, I'm a robot!");
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({ "org.matrix.msc1767.text": "Hello, I'm a robot!" })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_event_stable_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.message": [
|
||||
{ "body": "Hello, I'm a <em>robot</em>!", "mimetype": "text/html"},
|
||||
{ "body": "Hello, I'm a robot!" },
|
||||
]
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.notice",
|
||||
});
|
||||
|
||||
let message_event = assert_matches!(
|
||||
from_json_value::<AnyMessageLikeEvent>(json_data),
|
||||
Ok(AnyMessageLikeEvent::Notice(MessageLikeEvent::Original(message_event))) => message_event
|
||||
);
|
||||
|
||||
assert_eq!(message_event.event_id, "$event:notareal.hs");
|
||||
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(134_829_848)));
|
||||
assert_eq!(message_event.room_id, "!roomid:notareal.hs");
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
|
||||
let message = message_event.content.message;
|
||||
assert_eq!(message.find_plain(), Some("Hello, I'm a robot!"));
|
||||
assert_eq!(message.find_html(), Some("Hello, I'm a <em>robot</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notice_event_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.message": [
|
||||
{ "body": "Hello, I'm a <em>robot</em>!", "mimetype": "text/html"},
|
||||
{ "body": "Hello, I'm a robot!" },
|
||||
]
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.notice",
|
||||
});
|
||||
|
||||
let message_event = assert_matches!(
|
||||
from_json_value::<AnyMessageLikeEvent>(json_data),
|
||||
Ok(AnyMessageLikeEvent::Notice(MessageLikeEvent::Original(message_event))) => message_event
|
||||
);
|
||||
|
||||
assert_eq!(message_event.event_id, "$event:notareal.hs");
|
||||
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(134_829_848)));
|
||||
assert_eq!(message_event.room_id, "!roomid:notareal.hs");
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
|
||||
let message = message_event.content.message;
|
||||
assert_eq!(message.find_plain(), Some("Hello, I'm a robot!"));
|
||||
assert_eq!(message.find_html(), Some("Hello, I'm a <em>robot</em>!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-msc3954")]
|
||||
fn emote_event_serialization() {
|
||||
let content =
|
||||
EmoteEventContent::html("is testing some code…", "is testing some <code>code</code>…");
|
||||
@ -376,23 +271,28 @@ fn emote_event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "is testing some <code>code</code>…",
|
||||
"org.matrix.msc1767.text": "is testing some code…",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "is testing some <code>code</code>…" },
|
||||
{ "body": "is testing some code…" },
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emote_event_stable_deserialization() {
|
||||
#[cfg(feature = "unstable-msc3954")]
|
||||
fn emote_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.text": "is testing some code…",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "is testing some code…" },
|
||||
],
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.emote",
|
||||
"type": "org.matrix.msc1767.emote",
|
||||
});
|
||||
|
||||
let message_event = assert_matches!(
|
||||
@ -406,75 +306,76 @@ fn emote_event_stable_deserialization() {
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
|
||||
let message = message_event.content.message;
|
||||
assert_eq!(message.find_plain(), Some("is testing some code…"));
|
||||
assert_eq!(message.find_html(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emote_event_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.text": "is testing some code…",
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.emote",
|
||||
});
|
||||
|
||||
let message_event = assert_matches!(
|
||||
from_json_value::<AnyMessageLikeEvent>(json_data),
|
||||
Ok(AnyMessageLikeEvent::Emote(MessageLikeEvent::Original(message_event))) => message_event
|
||||
);
|
||||
|
||||
assert_eq!(message_event.event_id, "$event:notareal.hs");
|
||||
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(134_829_848)));
|
||||
assert_eq!(message_event.room_id, "!roomid:notareal.hs");
|
||||
assert_eq!(message_event.sender, "@user:notareal.hs");
|
||||
assert!(message_event.unsigned.is_empty());
|
||||
|
||||
let message = message_event.content.message;
|
||||
assert_eq!(message.find_plain(), Some("is testing some code…"));
|
||||
assert_eq!(message.find_html(), None);
|
||||
let text = message_event.content.text;
|
||||
assert_eq!(text.find_plain(), Some("is testing some code…"));
|
||||
assert_eq!(text.find_html(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-msc3554")]
|
||||
fn lang_serialization() {
|
||||
let content = MessageContent::try_from(vec![
|
||||
assign!(Text::plain("Bonjour le monde !"), { lang: Some("fr".into()) }),
|
||||
assign!(Text::plain("Hallo Welt!"), { lang: Some("de".into()) }),
|
||||
assign!(Text::plain("Hello World!"), { lang: Some("en".into()) }),
|
||||
let content = TextContentBlock::try_from(vec![
|
||||
assign!(TextRepresentation::plain("Bonjour le monde !"), { lang: Some("fr".into()) }),
|
||||
assign!(TextRepresentation::plain("Hallo Welt!"), { lang: Some("de".into()) }),
|
||||
assign!(TextRepresentation::plain("Hello World!"), { lang: Some("en".into()) }),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.message": [
|
||||
{ "body": "Bonjour le monde !", "mimetype": "text/plain", "lang": "fr"},
|
||||
{ "body": "Hallo Welt!", "mimetype": "text/plain", "lang": "de"},
|
||||
{ "body": "Hello World!", "mimetype": "text/plain", "lang": "en"},
|
||||
]
|
||||
})
|
||||
json!([
|
||||
{ "body": "Bonjour le monde !", "lang": "fr"},
|
||||
{ "body": "Hallo Welt!", "lang": "de"},
|
||||
{ "body": "Hello World!", "lang": "en"},
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-msc3554")]
|
||||
fn lang_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.message": [
|
||||
{ "body": "Bonjour le monde !", "mimetype": "text/plain", "lang": "fr"},
|
||||
{ "body": "Hallo Welt!", "mimetype": "text/plain", "lang": "de"},
|
||||
{ "body": "Hello World!", "mimetype": "text/plain", "lang": "en"},
|
||||
]
|
||||
});
|
||||
let json_data = json!([
|
||||
{ "body": "Bonjour le monde !", "lang": "fr"},
|
||||
{ "body": "Hallo Welt!", "lang": "de"},
|
||||
{ "body": "Hello World!", "lang": "en"},
|
||||
]);
|
||||
|
||||
let content = from_json_value::<MessageContent>(json_data).unwrap();
|
||||
let content = from_json_value::<TextContentBlock>(json_data).unwrap();
|
||||
assert_eq!(content[0].lang.as_deref(), Some("fr"));
|
||||
assert_eq!(content[1].lang.as_deref(), Some("de"));
|
||||
assert_eq!(content[2].lang.as_deref(), Some("en"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
fn automated_content_serialization() {
|
||||
let mut message_event_content =
|
||||
MessageEventContent::plain("> <@test:example.com> test\n\ntest reply");
|
||||
message_event_content.automated = true;
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "> <@test:example.com> test\n\ntest reply" },
|
||||
],
|
||||
"org.matrix.msc1767.automated": true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
fn automated_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Hello, <em>New World</em>!" },
|
||||
],
|
||||
"org.matrix.msc1767.automated": true,
|
||||
});
|
||||
|
||||
let content = from_json_value::<MessageEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.text.find_plain(), None);
|
||||
assert_eq!(content.text.find_html(), Some("Hello, <em>New World</em>!"));
|
||||
assert!(content.automated);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use js_int::uint;
|
||||
use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
poll::{
|
||||
end::{PollEndContent, PollEndEventContent},
|
||||
response::{PollResponseContent, PollResponseEventContent},
|
||||
@ -24,8 +24,8 @@ use serde_json::{from_value as from_json_value, json, to_value as to_json_value}
|
||||
#[test]
|
||||
fn poll_answers_deserialization_valid() {
|
||||
let json_data = json!([
|
||||
{ "id": "aaa", "m.text": "First answer" },
|
||||
{ "id": "bbb", "m.text": "Second answer" },
|
||||
{ "id": "aaa", "org.matrix.msc1767.text": [{ "body": "First answer" }] },
|
||||
{ "id": "bbb", "org.matrix.msc1767.text": [{ "body": "Second answer" }] },
|
||||
]);
|
||||
|
||||
let answers = from_json_value::<PollAnswers>(json_data).unwrap();
|
||||
@ -35,28 +35,28 @@ fn poll_answers_deserialization_valid() {
|
||||
#[test]
|
||||
fn poll_answers_deserialization_truncate() {
|
||||
let json_data = json!([
|
||||
{ "id": "aaa", "m.text": "1st answer" },
|
||||
{ "id": "bbb", "m.text": "2nd answer" },
|
||||
{ "id": "ccc", "m.text": "3rd answer" },
|
||||
{ "id": "ddd", "m.text": "4th answer" },
|
||||
{ "id": "eee", "m.text": "5th answer" },
|
||||
{ "id": "fff", "m.text": "6th answer" },
|
||||
{ "id": "ggg", "m.text": "7th answer" },
|
||||
{ "id": "hhh", "m.text": "8th answer" },
|
||||
{ "id": "iii", "m.text": "9th answer" },
|
||||
{ "id": "jjj", "m.text": "10th answer" },
|
||||
{ "id": "kkk", "m.text": "11th answer" },
|
||||
{ "id": "lll", "m.text": "12th answer" },
|
||||
{ "id": "mmm", "m.text": "13th answer" },
|
||||
{ "id": "nnn", "m.text": "14th answer" },
|
||||
{ "id": "ooo", "m.text": "15th answer" },
|
||||
{ "id": "ppp", "m.text": "16th answer" },
|
||||
{ "id": "qqq", "m.text": "17th answer" },
|
||||
{ "id": "rrr", "m.text": "18th answer" },
|
||||
{ "id": "sss", "m.text": "19th answer" },
|
||||
{ "id": "ttt", "m.text": "20th answer" },
|
||||
{ "id": "uuu", "m.text": "21th answer" },
|
||||
{ "id": "vvv", "m.text": "22th answer" },
|
||||
{ "id": "aaa", "org.matrix.msc1767.text": [{ "body": "1st answer" }] },
|
||||
{ "id": "bbb", "org.matrix.msc1767.text": [{ "body": "2nd answer" }] },
|
||||
{ "id": "ccc", "org.matrix.msc1767.text": [{ "body": "3rd answer" }] },
|
||||
{ "id": "ddd", "org.matrix.msc1767.text": [{ "body": "4th answer" }] },
|
||||
{ "id": "eee", "org.matrix.msc1767.text": [{ "body": "5th answer" }] },
|
||||
{ "id": "fff", "org.matrix.msc1767.text": [{ "body": "6th answer" }] },
|
||||
{ "id": "ggg", "org.matrix.msc1767.text": [{ "body": "7th answer" }] },
|
||||
{ "id": "hhh", "org.matrix.msc1767.text": [{ "body": "8th answer" }] },
|
||||
{ "id": "iii", "org.matrix.msc1767.text": [{ "body": "9th answer" }] },
|
||||
{ "id": "jjj", "org.matrix.msc1767.text": [{ "body": "10th answer" }] },
|
||||
{ "id": "kkk", "org.matrix.msc1767.text": [{ "body": "11th answer" }] },
|
||||
{ "id": "lll", "org.matrix.msc1767.text": [{ "body": "12th answer" }] },
|
||||
{ "id": "mmm", "org.matrix.msc1767.text": [{ "body": "13th answer" }] },
|
||||
{ "id": "nnn", "org.matrix.msc1767.text": [{ "body": "14th answer" }] },
|
||||
{ "id": "ooo", "org.matrix.msc1767.text": [{ "body": "15th answer" }] },
|
||||
{ "id": "ppp", "org.matrix.msc1767.text": [{ "body": "16th answer" }] },
|
||||
{ "id": "qqq", "org.matrix.msc1767.text": [{ "body": "17th answer" }] },
|
||||
{ "id": "rrr", "org.matrix.msc1767.text": [{ "body": "18th answer" }] },
|
||||
{ "id": "sss", "org.matrix.msc1767.text": [{ "body": "19th answer" }] },
|
||||
{ "id": "ttt", "org.matrix.msc1767.text": [{ "body": "20th answer" }] },
|
||||
{ "id": "uuu", "org.matrix.msc1767.text": [{ "body": "21th answer" }] },
|
||||
{ "id": "vvv", "org.matrix.msc1767.text": [{ "body": "22th answer" }] },
|
||||
]);
|
||||
|
||||
let answers = from_json_value::<PollAnswers>(json_data).unwrap();
|
||||
@ -75,12 +75,12 @@ fn poll_answers_deserialization_not_enough() {
|
||||
#[test]
|
||||
fn start_content_serialization() {
|
||||
let event_content = PollStartEventContent::new(PollStartContent::new(
|
||||
MessageContent::plain("How's the weather?"),
|
||||
TextContentBlock::plain("How's the weather?"),
|
||||
PollKind::Undisclosed,
|
||||
vec![
|
||||
PollAnswer::new("not-bad".to_owned(), MessageContent::plain("Not bad…")),
|
||||
PollAnswer::new("fine".to_owned(), MessageContent::plain("Fine.")),
|
||||
PollAnswer::new("amazing".to_owned(), MessageContent::plain("Amazing!")),
|
||||
PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")),
|
||||
PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")),
|
||||
PollAnswer::new("amazing".to_owned(), TextContentBlock::plain("Amazing!")),
|
||||
]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
@ -90,12 +90,12 @@ fn start_content_serialization() {
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc3381.poll.start": {
|
||||
"question": { "org.matrix.msc1767.text": "How's the weather?" },
|
||||
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
|
||||
"kind": "org.matrix.msc3381.poll.undisclosed",
|
||||
"answers": [
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": "Not bad…"},
|
||||
{ "id": "fine", "org.matrix.msc1767.text": "Fine."},
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": "Amazing!"},
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
|
||||
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
|
||||
],
|
||||
},
|
||||
})
|
||||
@ -106,12 +106,12 @@ fn start_content_serialization() {
|
||||
fn start_event_serialization() {
|
||||
let content = PollStartEventContent::new(assign!(
|
||||
PollStartContent::new(
|
||||
MessageContent::plain("How's the weather?"),
|
||||
TextContentBlock::plain("How's the weather?"),
|
||||
PollKind::Disclosed,
|
||||
vec![
|
||||
PollAnswer::new("not-bad".to_owned(), MessageContent::plain("Not bad…")),
|
||||
PollAnswer::new("fine".to_owned(), MessageContent::plain("Fine.")),
|
||||
PollAnswer::new("amazing".to_owned(), MessageContent::plain("Amazing!")),
|
||||
PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")),
|
||||
PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")),
|
||||
PollAnswer::new("amazing".to_owned(), TextContentBlock::plain("Amazing!")),
|
||||
]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
@ -123,13 +123,13 @@ fn start_event_serialization() {
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc3381.poll.start": {
|
||||
"question": { "org.matrix.msc1767.text": "How's the weather?" },
|
||||
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
|
||||
"kind": "org.matrix.msc3381.poll.disclosed",
|
||||
"max_selections": 2,
|
||||
"answers": [
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": "Not bad…"},
|
||||
{ "id": "fine", "org.matrix.msc1767.text": "Fine."},
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": "Amazing!"},
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
|
||||
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
|
||||
]
|
||||
},
|
||||
})
|
||||
@ -137,17 +137,17 @@ fn start_event_serialization() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_event_unstable_deserialization() {
|
||||
fn start_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc3381.poll.start": {
|
||||
"question": { "org.matrix.msc1767.text": "How's the weather?" },
|
||||
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
|
||||
"kind": "org.matrix.msc3381.poll.undisclosed",
|
||||
"max_selections": 2,
|
||||
"answers": [
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": "Not bad…"},
|
||||
{ "id": "fine", "org.matrix.msc1767.text": "Fine."},
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": "Amazing!"},
|
||||
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
|
||||
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
|
||||
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
|
||||
]
|
||||
},
|
||||
},
|
||||
@ -164,57 +164,17 @@ fn start_event_unstable_deserialization() {
|
||||
AnyMessageLikeEvent::PollStart(MessageLikeEvent::Original(message_event)) => message_event
|
||||
);
|
||||
let poll_start = message_event.content.poll_start;
|
||||
assert_eq!(poll_start.question[0].body, "How's the weather?");
|
||||
assert_eq!(poll_start.question.text[0].body, "How's the weather?");
|
||||
assert_eq!(poll_start.kind, PollKind::Undisclosed);
|
||||
assert_eq!(poll_start.max_selections, uint!(2));
|
||||
let answers = poll_start.answers.answers();
|
||||
assert_eq!(answers.len(), 3);
|
||||
assert_eq!(answers[0].id, "not-bad");
|
||||
assert_eq!(answers[0].answer[0].body, "Not bad…");
|
||||
assert_eq!(answers[0].text[0].body, "Not bad…");
|
||||
assert_eq!(answers[1].id, "fine");
|
||||
assert_eq!(answers[1].answer[0].body, "Fine.");
|
||||
assert_eq!(answers[1].text[0].body, "Fine.");
|
||||
assert_eq!(answers[2].id, "amazing");
|
||||
assert_eq!(answers[2].answer[0].body, "Amazing!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_event_stable_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.poll.start": {
|
||||
"question": { "m.text": "How's the weather?" },
|
||||
"kind": "m.poll.disclosed",
|
||||
"answers": [
|
||||
{ "id": "not-bad", "m.text": "Not bad…"},
|
||||
{ "id": "fine", "m.text": "Fine."},
|
||||
{ "id": "amazing", "m.text": "Amazing!"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.poll.start",
|
||||
});
|
||||
|
||||
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||
let message_event = assert_matches!(
|
||||
event,
|
||||
AnyMessageLikeEvent::PollStart(MessageLikeEvent::Original(message_event)) => message_event
|
||||
);
|
||||
let poll_start = message_event.content.poll_start;
|
||||
assert_eq!(poll_start.question[0].body, "How's the weather?");
|
||||
assert_eq!(poll_start.kind, PollKind::Disclosed);
|
||||
assert_eq!(poll_start.max_selections, uint!(1));
|
||||
let answers = poll_start.answers.answers();
|
||||
assert_eq!(answers.len(), 3);
|
||||
assert_eq!(answers[0].id, "not-bad");
|
||||
assert_eq!(answers[0].answer[0].body, "Not bad…");
|
||||
assert_eq!(answers[1].id, "fine");
|
||||
assert_eq!(answers[1].answer[0].body, "Fine.");
|
||||
assert_eq!(answers[2].id, "amazing");
|
||||
assert_eq!(answers[2].answer[0].body, "Amazing!");
|
||||
assert_eq!(answers[2].text[0].body, "Amazing!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -10,7 +10,7 @@ use ruma_common::{
|
||||
events::{
|
||||
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||
image::{ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo},
|
||||
message::MessageContent,
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::{message::Relation, JsonWebKeyInit},
|
||||
video::{VideoContent, VideoEventContent},
|
||||
@ -32,7 +32,9 @@ fn plain_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_video.webm",
|
||||
"org.matrix.msc1767.text": [
|
||||
{"body": "Upload: my_video.webm" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -72,7 +74,9 @@ fn encrypted_content_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_video.webm",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_video.webm" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -96,8 +100,8 @@ fn encrypted_content_serialization() {
|
||||
#[test]
|
||||
fn event_serialization() {
|
||||
let content = assign!(
|
||||
VideoEventContent::with_message(
|
||||
MessageContent::html(
|
||||
VideoEventContent::new(
|
||||
TextContentBlock::html(
|
||||
"Upload: my_lava_lamp.webm",
|
||||
"Upload: <strong>my_lava_lamp.webm</strong>",
|
||||
),
|
||||
@ -132,7 +136,7 @@ fn event_serialization() {
|
||||
),
|
||||
None
|
||||
)],
|
||||
caption: Some(MessageContent::plain("This is my awesome vintage lava lamp")),
|
||||
caption: TextContentBlock::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()),
|
||||
}),
|
||||
@ -142,8 +146,10 @@ fn event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.html": "Upload: <strong>my_lava_lamp.webm</strong>",
|
||||
"org.matrix.msc1767.text": "Upload: my_lava_lamp.webm",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "mimetype": "text/html", "body": "Upload: <strong>my_lava_lamp.webm</strong>" },
|
||||
{ "body": "Upload: my_lava_lamp.webm" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_lava_lamp.webm",
|
||||
@ -165,7 +171,6 @@ fn event_serialization() {
|
||||
"m.caption": [
|
||||
{
|
||||
"body": "This is my awesome vintage lava lamp",
|
||||
"mimetype": "text/plain",
|
||||
}
|
||||
],
|
||||
"m.relates_to": {
|
||||
@ -180,7 +185,9 @@ fn event_serialization() {
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Video: my_cat.mp4",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Video: my_cat.mp4" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
@ -195,23 +202,24 @@ fn plain_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<VideoEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Video: my_cat.mp4"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Video: my_cat.mp4"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_matches!(content.file.encryption_info, None);
|
||||
assert_eq!(content.video.width, None);
|
||||
assert_eq!(content.video.height, None);
|
||||
assert_eq!(content.video.duration, Some(Duration::from_millis(5_668)));
|
||||
assert_eq!(content.thumbnail.len(), 0);
|
||||
let caption = content.caption.unwrap();
|
||||
assert_eq!(caption.find_plain(), Some("Look at my cat!"));
|
||||
assert_eq!(caption.find_html(), None);
|
||||
assert_eq!(content.caption.find_plain(), Some("Look at my cat!"));
|
||||
assert_eq!(content.caption.find_html(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"m.text": "Video: my_cat.mp4",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Video: my_cat.mp4" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
@ -236,8 +244,8 @@ fn encrypted_content_deserialization() {
|
||||
});
|
||||
|
||||
let content = from_json_value::<VideoEventContent>(json_data).unwrap();
|
||||
assert_eq!(content.message.find_plain(), Some("Video: my_cat.mp4"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Video: my_cat.mp4"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert!(content.file.encryption_info.is_some());
|
||||
assert_eq!(content.video.width, None);
|
||||
@ -245,14 +253,16 @@ fn encrypted_content_deserialization() {
|
||||
assert_eq!(content.video.duration, None);
|
||||
assert_eq!(content.thumbnail.len(), 1);
|
||||
assert_eq!(content.thumbnail[0].file.url, "mxc://notareal.hs/thumbnail");
|
||||
assert_matches!(content.caption, None);
|
||||
assert!(content.caption.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.text": "Upload: my_gnome.webm",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Upload: my_gnome.webm" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_gnome.webm",
|
||||
@ -282,8 +292,8 @@ fn message_event_deserialization() {
|
||||
assert!(ev.unsigned.is_empty());
|
||||
|
||||
let content = ev.content;
|
||||
assert_eq!(content.message.find_plain(), Some("Upload: my_gnome.webm"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Upload: my_gnome.webm"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_eq!(content.video.width, Some(uint!(1300)));
|
||||
assert_eq!(content.video.height, Some(uint!(837)));
|
||||
|
@ -54,7 +54,9 @@ fn event_serialization() {
|
||||
assert_eq!(
|
||||
to_json_value(&content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Voice message",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Voice message" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "voice_message.ogg",
|
||||
@ -78,7 +80,9 @@ fn event_serialization() {
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"m.text": "Voice message",
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Voice message" },
|
||||
],
|
||||
"m.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "voice_message.ogg",
|
||||
@ -108,8 +112,8 @@ fn message_event_deserialization() {
|
||||
assert!(ev.unsigned.is_empty());
|
||||
|
||||
let content = ev.content;
|
||||
assert_eq!(content.message.find_plain(), Some("Voice message"));
|
||||
assert_eq!(content.message.find_html(), None);
|
||||
assert_eq!(content.text.find_plain(), Some("Voice message"));
|
||||
assert_eq!(content.text.find_html(), None);
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_eq!(content.audio.duration, Some(Duration::from_millis(5_300)));
|
||||
assert_matches!(content.audio.waveform, None);
|
||||
|
@ -117,6 +117,8 @@ unstable-extensible-events = [
|
||||
"unstable-msc3246",
|
||||
"unstable-msc3488",
|
||||
"unstable-msc3553",
|
||||
"unstable-msc3954",
|
||||
"unstable-msc3955",
|
||||
]
|
||||
unstable-msc1767 = ["ruma-common/unstable-msc1767"]
|
||||
unstable-msc2246 = ["ruma-client-api?/unstable-msc2246"]
|
||||
@ -153,6 +155,9 @@ unstable-msc3618 = ["ruma-federation-api?/unstable-msc3618"]
|
||||
unstable-msc3723 = ["ruma-federation-api?/unstable-msc3723"]
|
||||
unstable-msc3931 = ["ruma-common/unstable-msc3931"]
|
||||
unstable-msc3932 = ["ruma-common/unstable-msc3932"]
|
||||
unstable-msc3954 = ["ruma-common/unstable-msc3954"]
|
||||
unstable-msc3955 = ["ruma-common/unstable-msc3955"]
|
||||
unstable-msc3956 = ["ruma-common/unstable-msc3956"]
|
||||
unstable-pdu = ["ruma-common/unstable-pdu"]
|
||||
unstable-sanitize = ["ruma-common/unstable-sanitize"]
|
||||
unstable-unspecified = [
|
||||
@ -190,6 +195,9 @@ __ci = [
|
||||
"unstable-msc3618",
|
||||
"unstable-msc3723",
|
||||
"unstable-msc3932",
|
||||
"unstable-msc3954",
|
||||
"unstable-msc3955",
|
||||
"unstable-msc3956",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
Loading…
x
Reference in New Issue
Block a user