common: Add support for extensible image events
This commit is contained in:
parent
a54cf06b9e
commit
e336db767a
@ -34,6 +34,7 @@ unstable-msc2675 = []
|
|||||||
unstable-msc2676 = []
|
unstable-msc2676 = []
|
||||||
unstable-msc2677 = []
|
unstable-msc2677 = []
|
||||||
unstable-msc3551 = ["unstable-msc1767"]
|
unstable-msc3551 = ["unstable-msc1767"]
|
||||||
|
unstable-msc3552 = ["unstable-msc1767", "unstable-msc3551"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
@ -154,6 +154,8 @@ pub mod file;
|
|||||||
pub mod forwarded_room_key;
|
pub mod forwarded_room_key;
|
||||||
pub mod fully_read;
|
pub mod fully_read;
|
||||||
pub mod ignored_user_list;
|
pub mod ignored_user_list;
|
||||||
|
#[cfg(feature = "unstable-msc3552")]
|
||||||
|
pub mod image;
|
||||||
pub mod key;
|
pub mod key;
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
@ -41,6 +41,8 @@ event_enum! {
|
|||||||
"m.emote",
|
"m.emote",
|
||||||
#[cfg(feature = "unstable-msc3551")]
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
"m.file",
|
"m.file",
|
||||||
|
#[cfg(feature = "unstable-msc3552")]
|
||||||
|
"m.image",
|
||||||
"m.key.verification.ready",
|
"m.key.verification.ready",
|
||||||
"m.key.verification.start",
|
"m.key.verification.start",
|
||||||
"m.key.verification.cancel",
|
"m.key.verification.cancel",
|
||||||
@ -368,6 +370,8 @@ impl AnyMessageLikeEventContent {
|
|||||||
Self::Emote(ev) => ev.relates_to.clone().map(Into::into),
|
Self::Emote(ev) => ev.relates_to.clone().map(Into::into),
|
||||||
#[cfg(feature = "unstable-msc3551")]
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
Self::File(ev) => ev.relates_to.clone().map(Into::into),
|
Self::File(ev) => ev.relates_to.clone().map(Into::into),
|
||||||
|
#[cfg(feature = "unstable-msc3552")]
|
||||||
|
Self::Image(ev) => ev.relates_to.clone().map(Into::into),
|
||||||
Self::CallAnswer(_)
|
Self::CallAnswer(_)
|
||||||
| Self::CallInvite(_)
|
| Self::CallInvite(_)
|
||||||
| Self::CallHangup(_)
|
| Self::CallHangup(_)
|
||||||
|
275
crates/ruma-common/src/events/image.rs
Normal file
275
crates/ruma-common/src/events/image.rs
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
//! Types for extensible image message events ([MSC3552]).
|
||||||
|
//!
|
||||||
|
//! [MSC3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use ruma_macros::EventContent;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
file::{EncryptedContent, FileContent},
|
||||||
|
message::{MessageContent, Text},
|
||||||
|
room::message::Relation,
|
||||||
|
};
|
||||||
|
use crate::MxcUri;
|
||||||
|
|
||||||
|
/// The payload for an extensible image message.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[ruma_event(type = "m.image", kind = MessageLike)]
|
||||||
|
pub struct ImageEventContent {
|
||||||
|
/// The text representation of the message.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub message: MessageContent,
|
||||||
|
|
||||||
|
/// The file content of the message.
|
||||||
|
#[serde(rename = "org.matrix.msc1767.file")]
|
||||||
|
pub file: FileContent,
|
||||||
|
|
||||||
|
/// The image content of the message.
|
||||||
|
#[serde(rename = "org.matrix.msc1767.image")]
|
||||||
|
pub image: Box<ImageContent>,
|
||||||
|
|
||||||
|
/// The thumbnails of the message.
|
||||||
|
#[serde(
|
||||||
|
rename = "org.matrix.msc1767.thumbnail",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Thumbnails::is_empty"
|
||||||
|
)]
|
||||||
|
pub thumbnail: Thumbnails,
|
||||||
|
|
||||||
|
/// The thumbnails of the message.
|
||||||
|
#[serde(
|
||||||
|
rename = "org.matrix.msc1767.caption",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Captions::is_empty"
|
||||||
|
)]
|
||||||
|
pub caption: Captions,
|
||||||
|
|
||||||
|
/// Information about related messages for [rich replies].
|
||||||
|
///
|
||||||
|
/// [rich replies]: https://spec.matrix.org/v1.2/client-server-api/#rich-replies
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub relates_to: Option<Relation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageEventContent {
|
||||||
|
/// Creates a new `ImageEventContent` with the given plain text message and file.
|
||||||
|
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||||
|
Self {
|
||||||
|
message: MessageContent::plain(message),
|
||||||
|
file,
|
||||||
|
image: Default::default(),
|
||||||
|
thumbnail: Default::default(),
|
||||||
|
caption: Default::default(),
|
||||||
|
relates_to: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new non-encrypted `ImageEventContent` with the given message and file.
|
||||||
|
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||||
|
Self {
|
||||||
|
message,
|
||||||
|
file,
|
||||||
|
image: Default::default(),
|
||||||
|
thumbnail: Default::default(),
|
||||||
|
caption: Default::default(),
|
||||||
|
relates_to: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a thumbnail file content.
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct ThumbnailFileContentInfo {
|
||||||
|
/// The mimetype of the thumbnail, e.g. `image/png`.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mimetype: Option<String>,
|
||||||
|
|
||||||
|
/// The size of the thumbnail in bytes.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub size: Option<UInt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThumbnailFileContentInfo {
|
||||||
|
/// Creates an empty `ThumbnailFileContentInfo`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thumbnail file content.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct ThumbnailFileContent {
|
||||||
|
/// The URL to the thumbnail.
|
||||||
|
pub url: Box<MxcUri>,
|
||||||
|
|
||||||
|
/// Information about the uploaded thumbnail.
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub info: Option<Box<ThumbnailFileContentInfo>>,
|
||||||
|
|
||||||
|
/// Information on the encrypted thumbnail.
|
||||||
|
///
|
||||||
|
/// Required if the thumbnail is encrypted.
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub encryption_info: Option<Box<EncryptedContent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThumbnailFileContent {
|
||||||
|
/// Creates a new non-encrypted `ThumbnailFileContent` with the given url and file info.
|
||||||
|
pub fn plain(url: Box<MxcUri>, info: Option<Box<ThumbnailFileContentInfo>>) -> Self {
|
||||||
|
Self { url, info, encryption_info: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new encrypted `ThumbnailFileContent` with the given url, encryption info and
|
||||||
|
/// thumbnail file info.
|
||||||
|
pub fn encrypted(
|
||||||
|
url: Box<MxcUri>,
|
||||||
|
encryption_info: EncryptedContent,
|
||||||
|
info: Option<Box<ThumbnailFileContentInfo>>,
|
||||||
|
) -> Self {
|
||||||
|
Self { url, info, encryption_info: Some(Box::new(encryption_info)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the thumbnail file is encrypted.
|
||||||
|
pub fn is_encrypted(&self) -> bool {
|
||||||
|
self.encryption_info.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thumbnail content.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct ThumbnailContent {
|
||||||
|
/// The file info of the thumbnail.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub file: ThumbnailFileContent,
|
||||||
|
|
||||||
|
/// The image info of the thumbnail.
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub image: Option<Box<ImageContent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThumbnailContent {
|
||||||
|
/// Creates a `ThumbnailContent` with the given file and image info.
|
||||||
|
pub fn new(file: ThumbnailFileContent, image: Option<Box<ImageContent>>) -> Self {
|
||||||
|
Self { file, image }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An array of thumbnails.
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Thumbnails(pub(crate) Vec<ThumbnailContent>);
|
||||||
|
|
||||||
|
impl Thumbnails {
|
||||||
|
/// Creates a new `Thumbnails` with the given thumbnails.
|
||||||
|
///
|
||||||
|
/// The thumbnails must be ordered by most preferred first.
|
||||||
|
pub fn new(thumbnails: &[ThumbnailContent]) -> Self {
|
||||||
|
Self(thumbnails.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the thumbnails.
|
||||||
|
///
|
||||||
|
/// The thumbnails are ordered by most preferred first.
|
||||||
|
pub fn thumbnails(&self) -> &[ThumbnailContent] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An array of captions.
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Captions(pub(crate) Vec<Text>);
|
||||||
|
|
||||||
|
impl Captions {
|
||||||
|
/// Creates a new `Captions` with the given captions.
|
||||||
|
///
|
||||||
|
/// The captions must be ordered by most preferred first.
|
||||||
|
pub fn new(captions: &[Text]) -> Self {
|
||||||
|
Self(captions.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience constructor to create a plain text caption.
|
||||||
|
pub fn plain(body: impl Into<String>) -> Self {
|
||||||
|
Self(vec![Text::plain(body)])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience constructor to create an HTML caption.
|
||||||
|
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||||
|
Self(vec![Text::html(html_body), Text::plain(body)])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience constructor to create a Markdown caption.
|
||||||
|
///
|
||||||
|
/// Returns an HTML caption if some Markdown formatting was detected, otherwise returns a plain
|
||||||
|
/// text caption.
|
||||||
|
#[cfg(feature = "markdown")]
|
||||||
|
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||||
|
let mut message = Vec::with_capacity(2);
|
||||||
|
if let Some(html_body) = Text::markdown(&body) {
|
||||||
|
message.push(html_body);
|
||||||
|
}
|
||||||
|
message.push(Text::plain(body));
|
||||||
|
Self(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the captions.
|
||||||
|
///
|
||||||
|
/// The captions are ordered by most preferred first.
|
||||||
|
pub fn captions(&self) -> &[Text] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the plain text representation of this caption.
|
||||||
|
pub fn find_plain(&self) -> Option<&str> {
|
||||||
|
self.captions()
|
||||||
|
.iter()
|
||||||
|
.find(|content| content.mimetype == "text/plain")
|
||||||
|
.map(|content| content.body.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the HTML representation of this caption.
|
||||||
|
pub fn find_html(&self) -> Option<&str> {
|
||||||
|
self.captions()
|
||||||
|
.iter()
|
||||||
|
.find(|content| content.mimetype == "text/html")
|
||||||
|
.map(|content| content.body.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Image content.
|
||||||
|
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct ImageContent {
|
||||||
|
/// The height of the image in pixels.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub height: Option<UInt>,
|
||||||
|
|
||||||
|
/// The width of the image in pixels.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub width: Option<UInt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageContent {
|
||||||
|
/// Creates a new empty `ImageContent`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `ImageContent` with the given width and height.
|
||||||
|
pub fn with_size(width: UInt, height: UInt) -> Self {
|
||||||
|
Self { height: Some(height), width: Some(width) }
|
||||||
|
}
|
||||||
|
}
|
322
crates/ruma-common/tests/events/image.rs
Normal file
322
crates/ruma-common/tests/events/image.rs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
#![cfg(feature = "unstable-msc3552")]
|
||||||
|
|
||||||
|
use assign::assign;
|
||||||
|
use js_int::uint;
|
||||||
|
use matches::assert_matches;
|
||||||
|
use ruma_common::{
|
||||||
|
event_id,
|
||||||
|
events::{
|
||||||
|
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||||
|
image::{
|
||||||
|
Captions, ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent,
|
||||||
|
ThumbnailFileContentInfo, Thumbnails,
|
||||||
|
},
|
||||||
|
message::MessageContent,
|
||||||
|
room::{
|
||||||
|
message::{InReplyTo, Relation},
|
||||||
|
JsonWebKeyInit,
|
||||||
|
},
|
||||||
|
AnyMessageLikeEvent, MessageLikeEvent, Unsigned,
|
||||||
|
},
|
||||||
|
mxc_uri, room_id,
|
||||||
|
serde::Base64,
|
||||||
|
user_id, MilliSecondsSinceUnixEpoch,
|
||||||
|
};
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_content_serialization() {
|
||||||
|
let event_content = ImageEventContent::plain(
|
||||||
|
"Upload: my_image.jpg",
|
||||||
|
FileContent::plain(mxc_uri!("mxc://notareal.hs/abcdef").to_owned(), None),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_image.jpg",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypted_content_serialization() {
|
||||||
|
let event_content = ImageEventContent::plain(
|
||||||
|
"Upload: my_image.jpg",
|
||||||
|
FileContent::encrypted(
|
||||||
|
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||||
|
EncryptedContentInit {
|
||||||
|
key: JsonWebKeyInit {
|
||||||
|
kty: "oct".to_owned(),
|
||||||
|
key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
|
||||||
|
alg: "A256CTR".to_owned(),
|
||||||
|
k: Base64::parse("TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A").unwrap(),
|
||||||
|
ext: true,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
iv: Base64::parse("S22dq3NAX8wAAAAAAAAAAA").unwrap(),
|
||||||
|
hashes: [(
|
||||||
|
"sha256".to_owned(),
|
||||||
|
Base64::parse("aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q").unwrap(),
|
||||||
|
)]
|
||||||
|
.into(),
|
||||||
|
v: "v2".to_owned(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_image.jpg",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"key": {
|
||||||
|
"kty": "oct",
|
||||||
|
"key_ops": ["encrypt", "decrypt"],
|
||||||
|
"alg": "A256CTR",
|
||||||
|
"k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
|
||||||
|
"ext": true
|
||||||
|
},
|
||||||
|
"iv": "S22dq3NAX8wAAAAAAAAAAA",
|
||||||
|
"hashes": {
|
||||||
|
"sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
|
||||||
|
},
|
||||||
|
"v": "v2"
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn image_event_serialization() {
|
||||||
|
let event = MessageLikeEvent {
|
||||||
|
content: assign!(
|
||||||
|
ImageEventContent::with_message(
|
||||||
|
MessageContent::html(
|
||||||
|
"Upload: my_house.jpg",
|
||||||
|
"Upload: <strong>my_house.jpg</strong>",
|
||||||
|
),
|
||||||
|
FileContent::plain(
|
||||||
|
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||||
|
Some(Box::new(assign!(
|
||||||
|
FileContentInfo::new(),
|
||||||
|
{
|
||||||
|
name: Some("my_house.jpg".to_owned()),
|
||||||
|
mimetype: Some("image/jpeg".to_owned()),
|
||||||
|
size: Some(uint!(897_774)),
|
||||||
|
}
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
{
|
||||||
|
image: Box::new(ImageContent::with_size(uint!(1920), uint!(1080))),
|
||||||
|
thumbnail: Thumbnails::new(&[ThumbnailContent::new(
|
||||||
|
ThumbnailFileContent::plain(
|
||||||
|
mxc_uri!("mxc://notareal.hs/thumbnail").to_owned(),
|
||||||
|
Some(Box::new(assign!(ThumbnailFileContentInfo::new(), {
|
||||||
|
mimetype: Some("image/jpeg".to_owned()),
|
||||||
|
size: Some(uint!(334_593)),
|
||||||
|
})))
|
||||||
|
),
|
||||||
|
None
|
||||||
|
)]),
|
||||||
|
caption: Captions::plain("This is my house"),
|
||||||
|
relates_to: Some(Relation::Reply {
|
||||||
|
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: Unsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"org.matrix.msc1767.message": [
|
||||||
|
{ "body": "Upload: <strong>my_house.jpg</strong>", "mimetype": "text/html"},
|
||||||
|
{ "body": "Upload: my_house.jpg", "mimetype": "text/plain"},
|
||||||
|
],
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"name": "my_house.jpg",
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"size": 897_774,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.thumbnail": [
|
||||||
|
{
|
||||||
|
"url": "mxc://notareal.hs/thumbnail",
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"size": 334_593,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"org.matrix.msc1767.caption": [
|
||||||
|
{
|
||||||
|
"body": "This is my house",
|
||||||
|
"mimetype": "text/plain",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$replyevent:example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.image",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_content_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_cat.png",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {
|
||||||
|
"width": 668,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.caption": [
|
||||||
|
{
|
||||||
|
"body": "Look at my cat!",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<ImageEventContent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
ImageEventContent { message, file, image, thumbnail, caption, .. }
|
||||||
|
if message.find_plain() == Some("Upload: my_cat.png")
|
||||||
|
&& message.find_html().is_none()
|
||||||
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
|
&& image.width == Some(uint!(668))
|
||||||
|
&& image.height.is_none()
|
||||||
|
&& thumbnail.is_empty()
|
||||||
|
&& caption.find_plain() == Some("Look at my cat!")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypted_content_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"key": {
|
||||||
|
"kty": "oct",
|
||||||
|
"key_ops": ["encrypt", "decrypt"],
|
||||||
|
"alg": "A256CTR",
|
||||||
|
"k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
|
||||||
|
"ext": true
|
||||||
|
},
|
||||||
|
"iv": "S22dq3NAX8wAAAAAAAAAAA",
|
||||||
|
"hashes": {
|
||||||
|
"sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
|
||||||
|
},
|
||||||
|
"v": "v2"
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {},
|
||||||
|
"org.matrix.msc1767.thumbnail": [
|
||||||
|
{
|
||||||
|
"url": "mxc://notareal.hs/thumbnail",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<ImageEventContent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
ImageEventContent { message, file, image, thumbnail, caption, .. }
|
||||||
|
if message.find_plain() == Some("Upload: my_file.txt")
|
||||||
|
&& message.find_html().is_none()
|
||||||
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
|
&& file.encryption_info.is_some()
|
||||||
|
&& image.width.is_none()
|
||||||
|
&& image.height.is_none()
|
||||||
|
&& thumbnail.thumbnails()[0].file.url == "mxc://notareal.hs/thumbnail"
|
||||||
|
&& caption.is_empty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn message_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_gnome.webp",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"name": "my_gnome.webp",
|
||||||
|
"mimetype": "image/webp",
|
||||||
|
"size": 123_774,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.image": {
|
||||||
|
"width": 1300,
|
||||||
|
"height": 837,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.image",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
||||||
|
AnyMessageLikeEvent::Image(MessageLikeEvent {
|
||||||
|
content: ImageEventContent {
|
||||||
|
message,
|
||||||
|
file: FileContent {
|
||||||
|
url,
|
||||||
|
info: Some(info),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
image,
|
||||||
|
thumbnail,
|
||||||
|
caption,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
event_id,
|
||||||
|
origin_server_ts,
|
||||||
|
room_id,
|
||||||
|
sender,
|
||||||
|
unsigned
|
||||||
|
}) if event_id == event_id!("$event:notareal.hs")
|
||||||
|
&& message.find_plain() == Some("Upload: my_gnome.webp")
|
||||||
|
&& message.find_html().is_none()
|
||||||
|
&& url == "mxc://notareal.hs/abcdef"
|
||||||
|
&& info.name.as_deref() == Some("my_gnome.webp")
|
||||||
|
&& info.mimetype.as_deref() == Some("image/webp")
|
||||||
|
&& info.size == Some(uint!(123_774))
|
||||||
|
&& image.width == Some(uint!(1300))
|
||||||
|
&& image.height == Some(uint!(837))
|
||||||
|
&& thumbnail.is_empty()
|
||||||
|
&& caption.is_empty()
|
||||||
|
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
||||||
|
&& room_id == room_id!("!roomid:notareal.hs")
|
||||||
|
&& sender == user_id!("@user:notareal.hs")
|
||||||
|
&& unsigned.is_empty()
|
||||||
|
);
|
||||||
|
}
|
@ -8,6 +8,7 @@ mod event_content;
|
|||||||
mod event_content_enum;
|
mod event_content_enum;
|
||||||
mod event_enums;
|
mod event_enums;
|
||||||
mod file;
|
mod file;
|
||||||
|
mod image;
|
||||||
mod initial_state;
|
mod initial_state;
|
||||||
mod message;
|
mod message;
|
||||||
mod message_event;
|
mod message_event;
|
||||||
|
@ -118,6 +118,7 @@ unstable-msc2675 = ["ruma-common/unstable-msc2675"]
|
|||||||
unstable-msc2676 = ["ruma-common/unstable-msc2676"]
|
unstable-msc2676 = ["ruma-common/unstable-msc2676"]
|
||||||
unstable-msc2677 = ["ruma-common/unstable-msc2677"]
|
unstable-msc2677 = ["ruma-common/unstable-msc2677"]
|
||||||
unstable-msc3551 = ["ruma-common/unstable-msc3551"]
|
unstable-msc3551 = ["ruma-common/unstable-msc3551"]
|
||||||
|
unstable-msc3552 = ["ruma-common/unstable-msc3552"]
|
||||||
unstable-msc3618 = ["ruma-federation-api/unstable-msc3618"]
|
unstable-msc3618 = ["ruma-federation-api/unstable-msc3618"]
|
||||||
|
|
||||||
# Private feature, only used in test / benchmarking code
|
# Private feature, only used in test / benchmarking code
|
||||||
@ -130,6 +131,7 @@ __ci = [
|
|||||||
"unstable-msc2676",
|
"unstable-msc2676",
|
||||||
"unstable-msc2677",
|
"unstable-msc2677",
|
||||||
"unstable-msc3551",
|
"unstable-msc3551",
|
||||||
|
"unstable-msc3552",
|
||||||
"unstable-msc3618",
|
"unstable-msc3618",
|
||||||
"ruma-state-res/__ci",
|
"ruma-state-res/__ci",
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user