events: Update types according to changes in MSC3553
This commit is contained in:
parent
5985bbe803
commit
60f754322e
@ -85,7 +85,8 @@ event_enum! {
|
||||
"m.room.redaction" => super::room::redaction,
|
||||
"m.sticker" => super::sticker,
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
"m.video" => super::video,
|
||||
#[ruma_enum(alias = "m.video")]
|
||||
"org.matrix.msc1767.video" => super::video,
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
"m.voice" => super::voice,
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
file::FileContentBlock, image::ThumbnailContentBlock, message::TextContentBlock,
|
||||
file::{CaptionContentBlock, FileContentBlock},
|
||||
image::ThumbnailContentBlock,
|
||||
message::TextContentBlock,
|
||||
room::message::Relation,
|
||||
};
|
||||
|
||||
@ -22,7 +24,7 @@ use super::{
|
||||
/// [`message`]: super::message
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.video", kind = MessageLike, without_relation)]
|
||||
#[ruma_event(type = "org.matrix.msc1767.video", kind = MessageLike, without_relation)]
|
||||
pub struct VideoEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(rename = "org.matrix.msc1767.text")]
|
||||
@ -32,11 +34,13 @@ pub struct VideoEventContent {
|
||||
#[serde(rename = "org.matrix.msc1767.file")]
|
||||
pub file: FileContentBlock,
|
||||
|
||||
/// The video content of the message.
|
||||
#[serde(rename = "m.video")]
|
||||
pub video: Box<VideoContent>,
|
||||
/// The video details of the message, if any.
|
||||
#[serde(rename = "org.matrix.msc1767.video_details", skip_serializing_if = "Option::is_none")]
|
||||
pub video_details: Option<VideoDetailsContentBlock>,
|
||||
|
||||
/// The thumbnails of the message.
|
||||
/// The thumbnails of the message, if any.
|
||||
///
|
||||
/// This is optional and defaults to an empty array.
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.thumbnail",
|
||||
default,
|
||||
@ -44,9 +48,18 @@ pub struct VideoEventContent {
|
||||
)]
|
||||
pub thumbnail: ThumbnailContentBlock,
|
||||
|
||||
/// The captions of the message.
|
||||
#[serde(rename = "m.caption", default, skip_serializing_if = "TextContentBlock::is_empty")]
|
||||
pub caption: TextContentBlock,
|
||||
/// The caption of the message, if any.
|
||||
#[serde(rename = "org.matrix.msc1767.caption", skip_serializing_if = "Option::is_none")]
|
||||
pub caption: Option<CaptionContentBlock>,
|
||||
|
||||
/// 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(
|
||||
@ -63,56 +76,53 @@ impl VideoEventContent {
|
||||
Self {
|
||||
text,
|
||||
file,
|
||||
video: Default::default(),
|
||||
video_details: None,
|
||||
thumbnail: Default::default(),
|
||||
caption: Default::default(),
|
||||
caption: None,
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `VideoEventContent` with the given plain text fallback representation and
|
||||
/// file.
|
||||
pub fn plain(text: impl Into<String>, file: FileContentBlock) -> Self {
|
||||
pub fn with_plain_text(plain_text: impl Into<String>, file: FileContentBlock) -> Self {
|
||||
Self {
|
||||
text: TextContentBlock::plain(text),
|
||||
text: TextContentBlock::plain(plain_text),
|
||||
file,
|
||||
video: Default::default(),
|
||||
video_details: None,
|
||||
thumbnail: Default::default(),
|
||||
caption: Default::default(),
|
||||
caption: None,
|
||||
#[cfg(feature = "unstable-msc3955")]
|
||||
automated: false,
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Video content.
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
/// A block for details of video content.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct VideoContent {
|
||||
/// The height of the video in pixels.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub height: Option<UInt>,
|
||||
|
||||
pub struct VideoDetailsContentBlock {
|
||||
/// The width of the video in pixels.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub width: Option<UInt>,
|
||||
pub width: UInt,
|
||||
|
||||
/// The duration of the video in milliseconds.
|
||||
/// The height of the video in pixels.
|
||||
pub height: UInt,
|
||||
|
||||
/// The duration of the video in seconds.
|
||||
#[serde(
|
||||
with = "crate::serde::duration::opt_ms",
|
||||
with = "crate::serde::duration::opt_secs",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub duration: Option<Duration>,
|
||||
}
|
||||
|
||||
impl VideoContent {
|
||||
/// Creates a new empty `VideoContent`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Whether this `VideoContent` is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.height.is_none() && self.width.is_none() && self.duration.is_none()
|
||||
impl VideoDetailsContentBlock {
|
||||
/// Creates a new `VideoDetailsContentBlock` with the given height and width.
|
||||
pub fn new(width: UInt, height: UInt) -> Self {
|
||||
Self { width, height, duration: None }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! De-/serialization functions for `std::time::Duration` objects
|
||||
|
||||
pub mod opt_ms;
|
||||
pub mod opt_secs;
|
||||
pub mod secs;
|
||||
|
96
crates/ruma-common/src/serde/duration/opt_secs.rs
Normal file
96
crates/ruma-common/src/serde/duration/opt_secs.rs
Normal file
@ -0,0 +1,96 @@
|
||||
//! De-/serialization functions for `Option<std::time::Duration>` objects represented as
|
||||
//! milliseconds.
|
||||
//!
|
||||
//! Delegates to `js_int::UInt` to ensure integer size is within bounds.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use js_int::UInt;
|
||||
use serde::{
|
||||
de::{Deserialize, Deserializer},
|
||||
ser::{Error, Serialize, Serializer},
|
||||
};
|
||||
|
||||
/// Serialize an `Option<Duration>`.
|
||||
///
|
||||
/// Will fail if integer is greater than the maximum integer that can be
|
||||
/// unambiguously represented by an f64.
|
||||
pub fn serialize<S>(opt_duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match opt_duration {
|
||||
Some(duration) => match UInt::try_from(duration.as_secs()) {
|
||||
Ok(uint) => uint.serialize(serializer),
|
||||
Err(err) => Err(S::Error::custom(err)),
|
||||
},
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes an `Option<Duration>`.
|
||||
///
|
||||
/// Will fail if integer is greater than the maximum integer that can be
|
||||
/// unambiguously represented by an f64.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Option::<UInt>::deserialize(deserializer)?.map(|secs| Duration::from_secs(secs.into())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct DurationTest {
|
||||
#[serde(with = "super", default, skip_serializing_if = "Option::is_none")]
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_some() {
|
||||
let json = json!({ "timeout": 300 });
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<DurationTest>(json).unwrap(),
|
||||
DurationTest { timeout: Some(Duration::from_secs(300)) },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_none_by_absence() {
|
||||
let json = json!({});
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<DurationTest>(json).unwrap(),
|
||||
DurationTest { timeout: None },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_none_by_null() {
|
||||
let json = json!({ "timeout": null });
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<DurationTest>(json).unwrap(),
|
||||
DurationTest { timeout: None },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_some() {
|
||||
let request = DurationTest { timeout: Some(Duration::new(2, 0)) };
|
||||
assert_eq!(serde_json::to_value(request).unwrap(), json!({ "timeout": 2 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_none() {
|
||||
let request = DurationTest { timeout: None };
|
||||
assert_eq!(serde_json::to_value(request).unwrap(), json!({}));
|
||||
}
|
||||
}
|
@ -3,17 +3,16 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use assign::assign;
|
||||
use js_int::uint;
|
||||
use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
file::{EncryptedContentInit, FileContentBlock},
|
||||
file::{CaptionContentBlock, EncryptedContentInit, FileContentBlock},
|
||||
image::{Thumbnail, ThumbnailFileContentBlock, ThumbnailImageDetailsContentBlock},
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::{message::Relation, JsonWebKeyInit},
|
||||
video::{VideoContent, VideoEventContent},
|
||||
video::{VideoDetailsContentBlock, VideoEventContent},
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
},
|
||||
mxc_uri,
|
||||
@ -24,7 +23,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 = VideoEventContent::plain(
|
||||
let event_content = VideoEventContent::with_plain_text(
|
||||
"Upload: my_video.webm",
|
||||
FileContentBlock::plain(
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
@ -42,14 +41,13 @@ fn plain_content_serialization() {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_video.webm",
|
||||
},
|
||||
"m.video": {}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_serialization() {
|
||||
let event_content = VideoEventContent::plain(
|
||||
let event_content = VideoEventContent::with_plain_text(
|
||||
"Upload: my_video.webm",
|
||||
FileContentBlock::encrypted(
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
@ -97,7 +95,6 @@ fn encrypted_content_serialization() {
|
||||
},
|
||||
"v": "v2"
|
||||
},
|
||||
"m.video": {}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -117,14 +114,9 @@ fn event_serialization() {
|
||||
|
||||
content.file.mimetype = Some("video/webm".to_owned());
|
||||
content.file.size = Some(uint!(1_897_774));
|
||||
content.video = Box::new(assign!(
|
||||
VideoContent::new(),
|
||||
{
|
||||
width: Some(uint!(1920)),
|
||||
height: Some(uint!(1080)),
|
||||
duration: Some(Duration::from_secs(15)),
|
||||
}
|
||||
));
|
||||
let mut video_details = VideoDetailsContentBlock::new(uint!(1920), uint!(1080));
|
||||
video_details.duration = Some(Duration::from_secs(15));
|
||||
content.video_details = Some(video_details);
|
||||
let mut thumbnail = Thumbnail::new(
|
||||
ThumbnailFileContentBlock::plain(
|
||||
mxc_uri!("mxc://notareal.hs/thumbnail").to_owned(),
|
||||
@ -134,7 +126,7 @@ fn event_serialization() {
|
||||
);
|
||||
thumbnail.file.size = Some(uint!(334_593));
|
||||
content.thumbnail = vec![thumbnail].into();
|
||||
content.caption = TextContentBlock::plain("This is my awesome vintage lava lamp");
|
||||
content.caption = Some(CaptionContentBlock::plain("This is my awesome vintage lava lamp"));
|
||||
content.relates_to = Some(Relation::Reply {
|
||||
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||
});
|
||||
@ -152,10 +144,10 @@ fn event_serialization() {
|
||||
"mimetype": "video/webm",
|
||||
"size": 1_897_774,
|
||||
},
|
||||
"m.video": {
|
||||
"org.matrix.msc1767.video_details": {
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"duration": 15_000,
|
||||
"duration": 15,
|
||||
},
|
||||
"org.matrix.msc1767.thumbnail": [
|
||||
{
|
||||
@ -170,16 +162,16 @@ fn event_serialization() {
|
||||
},
|
||||
}
|
||||
],
|
||||
"m.caption": [
|
||||
{
|
||||
"body": "This is my awesome vintage lava lamp",
|
||||
}
|
||||
"org.matrix.msc1767.caption": {
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "This is my awesome vintage lava lamp" },
|
||||
],
|
||||
},
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$replyevent:example.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -194,14 +186,16 @@ fn plain_content_deserialization() {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_cat.mp4",
|
||||
},
|
||||
"m.video": {
|
||||
"org.matrix.msc1767.video_details": {
|
||||
"width": 720,
|
||||
"height": 480,
|
||||
"duration": 5_668,
|
||||
},
|
||||
"m.caption": [
|
||||
{
|
||||
"body": "Look at my cat!",
|
||||
}
|
||||
]
|
||||
"org.matrix.msc1767.caption": {
|
||||
"org.matrix.msc1767.text": [
|
||||
{ "body": "Look at my cat!" },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
let content = from_json_value::<VideoEventContent>(json_data).unwrap();
|
||||
@ -210,12 +204,14 @@ fn plain_content_deserialization() {
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_eq!(content.file.name, "my_cat.mp4");
|
||||
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)));
|
||||
let video_details = content.video_details.unwrap();
|
||||
assert_eq!(video_details.width, uint!(720));
|
||||
assert_eq!(video_details.height, uint!(480));
|
||||
assert_eq!(video_details.duration, Some(Duration::from_secs(5_668)));
|
||||
assert_eq!(content.thumbnail.len(), 0);
|
||||
assert_eq!(content.caption.find_plain(), Some("Look at my cat!"));
|
||||
assert_eq!(content.caption.find_html(), None);
|
||||
let caption = content.caption.unwrap();
|
||||
assert_eq!(caption.text.find_plain(), Some("Look at my cat!"));
|
||||
assert_eq!(caption.text.find_html(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -240,7 +236,6 @@ fn encrypted_content_deserialization() {
|
||||
},
|
||||
"v": "v2"
|
||||
},
|
||||
"m.video": {},
|
||||
"org.matrix.msc1767.thumbnail": [
|
||||
{
|
||||
"org.matrix.msc1767.file": {
|
||||
@ -261,16 +256,14 @@ fn encrypted_content_deserialization() {
|
||||
assert_eq!(content.file.url, "mxc://notareal.hs/abcdef");
|
||||
assert_eq!(content.file.name, "my_cat.mp4");
|
||||
assert!(content.file.encryption_info.is_some());
|
||||
assert_eq!(content.video.width, None);
|
||||
assert_eq!(content.video.height, None);
|
||||
assert_eq!(content.video.duration, None);
|
||||
assert!(content.video_details.is_none());
|
||||
assert_eq!(content.thumbnail.len(), 1);
|
||||
let thumbnail = &content.thumbnail[0];
|
||||
assert_eq!(thumbnail.file.url, "mxc://notareal.hs/thumbnail");
|
||||
assert_eq!(thumbnail.file.mimetype, "image/png");
|
||||
assert_eq!(thumbnail.image_details.width, uint!(560));
|
||||
assert_eq!(thumbnail.image_details.height, uint!(480));
|
||||
assert!(content.caption.is_empty());
|
||||
assert!(content.caption.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -286,7 +279,7 @@ fn message_event_deserialization() {
|
||||
"mimetype": "video/webm",
|
||||
"size": 123_774,
|
||||
},
|
||||
"m.video": {
|
||||
"org.matrix.msc1767.video_details": {
|
||||
"width": 1300,
|
||||
"height": 837,
|
||||
}
|
||||
@ -295,7 +288,7 @@ fn message_event_deserialization() {
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.video",
|
||||
"type": "org.matrix.msc1767.video",
|
||||
});
|
||||
|
||||
let ev = assert_matches!(
|
||||
@ -315,8 +308,9 @@ fn message_event_deserialization() {
|
||||
assert_eq!(content.file.name, "my_gnome.webm");
|
||||
assert_eq!(content.file.mimetype.as_deref(), Some("video/webm"));
|
||||
assert_eq!(content.file.size, Some(uint!(123_774)));
|
||||
assert_eq!(content.video.width, Some(uint!(1300)));
|
||||
assert_eq!(content.video.height, Some(uint!(837)));
|
||||
assert_eq!(content.video.duration, None);
|
||||
let video_details = content.video_details.unwrap();
|
||||
assert_eq!(video_details.width, uint!(1300));
|
||||
assert_eq!(video_details.height, uint!(837));
|
||||
assert_eq!(video_details.duration, None);
|
||||
assert_eq!(content.thumbnail.len(), 0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user