common: Add support for extensible audio events
This commit is contained in:
parent
24fd3f79f0
commit
5af2e38506
@ -33,6 +33,7 @@ unstable-msc2448 = []
|
|||||||
unstable-msc2675 = []
|
unstable-msc2675 = []
|
||||||
unstable-msc2676 = []
|
unstable-msc2676 = []
|
||||||
unstable-msc2677 = []
|
unstable-msc2677 = []
|
||||||
|
unstable-msc3246 = ["unstable-msc3551", "thiserror"]
|
||||||
unstable-msc3551 = ["unstable-msc1767"]
|
unstable-msc3551 = ["unstable-msc1767"]
|
||||||
unstable-msc3552 = ["unstable-msc1767", "unstable-msc3551"]
|
unstable-msc3552 = ["unstable-msc1767", "unstable-msc3551"]
|
||||||
unstable-msc3553 = ["unstable-msc3552"]
|
unstable-msc3553 = ["unstable-msc3552"]
|
||||||
|
@ -144,6 +144,8 @@ pub mod macros {
|
|||||||
pub use ruma_macros::{Event, EventContent};
|
pub use ruma_macros::{Event, EventContent};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3246")]
|
||||||
|
pub mod audio;
|
||||||
pub mod call;
|
pub mod call;
|
||||||
pub mod direct;
|
pub mod direct;
|
||||||
pub mod dummy;
|
pub mod dummy;
|
||||||
|
167
crates/ruma-common/src/events/audio.rs
Normal file
167
crates/ruma-common/src/events/audio.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
//! Types for extensible audio message events ([MSC3246]).
|
||||||
|
//!
|
||||||
|
//! [MSC3246]: https://github.com/matrix-org/matrix-spec-proposals/pull/3246
|
||||||
|
|
||||||
|
use std::{convert::TryFrom, time::Duration};
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use ruma_macros::EventContent;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
mod amplitude_serde;
|
||||||
|
mod waveform_serde;
|
||||||
|
|
||||||
|
use waveform_serde::WaveformSerDeHelper;
|
||||||
|
|
||||||
|
use super::{file::FileContent, message::MessageContent, room::message::Relation};
|
||||||
|
|
||||||
|
/// The payload for an extensible audio message.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[ruma_event(type = "m.audio", kind = MessageLike)]
|
||||||
|
pub struct AudioEventContent {
|
||||||
|
/// 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 audio content of the message.
|
||||||
|
#[serde(rename = "org.matrix.msc1767.audio")]
|
||||||
|
pub audio: AudioContent,
|
||||||
|
|
||||||
|
/// Information about related messages.
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub relates_to: Option<Relation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioEventContent {
|
||||||
|
/// Creates a new `AudioEventContent` with the given plain text message and file.
|
||||||
|
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||||
|
Self {
|
||||||
|
message: MessageContent::plain(message),
|
||||||
|
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.
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct AudioContent {
|
||||||
|
/// The duration of the video in milliseconds.
|
||||||
|
#[serde(
|
||||||
|
with = "ruma_common::serde::duration::opt_ms",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub duration: Option<Duration>,
|
||||||
|
|
||||||
|
/// The waveform representation of the audio content.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub waveform: Option<Waveform>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioContent {
|
||||||
|
/// Creates a new empty `AudioContent`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The waveform representation of audio content.
|
||||||
|
///
|
||||||
|
/// Must include between 30 and 120 `Amplitude`s.
|
||||||
|
///
|
||||||
|
/// To build this, use the `TryFrom` implementations.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(try_from = "WaveformSerDeHelper")]
|
||||||
|
pub struct Waveform(Vec<Amplitude>);
|
||||||
|
|
||||||
|
impl Waveform {
|
||||||
|
/// The smallest number of values contained in a `Waveform`.
|
||||||
|
pub const MIN_LENGTH: usize = 30;
|
||||||
|
|
||||||
|
/// The largest number of values contained in a `Waveform`.
|
||||||
|
pub const MAX_LENGTH: usize = 120;
|
||||||
|
|
||||||
|
/// The amplitudes of this `Waveform`.
|
||||||
|
pub fn amplitudes(&self) -> &[Amplitude] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error encountered when trying to convert to a `Waveform`.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum WaveformError {
|
||||||
|
/// There are more than [`Waveform::MAX`] values.
|
||||||
|
#[error("too many values")]
|
||||||
|
TooManyValues,
|
||||||
|
/// There are less that [`Waveform::MIN`] values.
|
||||||
|
#[error("not enough values")]
|
||||||
|
NotEnoughValues,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Vec<Amplitude>> for Waveform {
|
||||||
|
type Error = WaveformError;
|
||||||
|
|
||||||
|
fn try_from(value: Vec<Amplitude>) -> Result<Self, Self::Error> {
|
||||||
|
if value.len() < Self::MIN_LENGTH {
|
||||||
|
Err(WaveformError::NotEnoughValues)
|
||||||
|
} else if value.len() > Self::MAX_LENGTH {
|
||||||
|
Err(WaveformError::TooManyValues)
|
||||||
|
} else {
|
||||||
|
Ok(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[Amplitude]> for Waveform {
|
||||||
|
type Error = WaveformError;
|
||||||
|
|
||||||
|
fn try_from(value: &[Amplitude]) -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from(value.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The amplitude of a waveform sample.
|
||||||
|
///
|
||||||
|
/// Must be an integer between 0 and 1024.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
|
||||||
|
pub struct Amplitude(UInt);
|
||||||
|
|
||||||
|
impl Amplitude {
|
||||||
|
/// The smallest value that can be represented by this type, 0.
|
||||||
|
pub const MIN: u16 = 0;
|
||||||
|
|
||||||
|
/// The largest value that can be represented by this type, 1024.
|
||||||
|
pub const MAX: u16 = 1024;
|
||||||
|
|
||||||
|
/// Creates a new `Amplitude` with the given value.
|
||||||
|
///
|
||||||
|
/// It will saturate if it is bigger than [`Amplitude::MAX`].
|
||||||
|
pub fn new(value: u16) -> Self {
|
||||||
|
Self(value.min(Self::MAX).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value of this `Amplitude`.
|
||||||
|
pub fn value(&self) -> UInt {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for Amplitude {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
16
crates/ruma-common/src/events/audio/amplitude_serde.rs
Normal file
16
crates/ruma-common/src/events/audio/amplitude_serde.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//! `Serialize` and `Deserialize` implementations for extensible events (MSC1767).
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::Amplitude;
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Amplitude {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let uint = UInt::deserialize(deserializer)?;
|
||||||
|
Ok(Self(uint.min(Self::MAX.into())))
|
||||||
|
}
|
||||||
|
}
|
18
crates/ruma-common/src/events/audio/waveform_serde.rs
Normal file
18
crates/ruma-common/src/events/audio/waveform_serde.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//! `Serialize` and `Deserialize` implementations for extensible events (MSC1767).
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::{Amplitude, Waveform, WaveformError};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub(crate) struct WaveformSerDeHelper(Vec<Amplitude>);
|
||||||
|
|
||||||
|
impl TryFrom<WaveformSerDeHelper> for Waveform {
|
||||||
|
type Error = WaveformError;
|
||||||
|
|
||||||
|
fn try_from(helper: WaveformSerDeHelper) -> Result<Self, Self::Error> {
|
||||||
|
Waveform::try_from(helper.0)
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,8 @@ event_enum! {
|
|||||||
|
|
||||||
/// Any message-like event.
|
/// Any message-like event.
|
||||||
enum MessageLike {
|
enum MessageLike {
|
||||||
|
#[cfg(feature = "unstable-msc3246")]
|
||||||
|
"m.audio",
|
||||||
"m.call.answer",
|
"m.call.answer",
|
||||||
"m.call.invite",
|
"m.call.invite",
|
||||||
"m.call.hangup",
|
"m.call.hangup",
|
||||||
@ -370,6 +372,8 @@ impl AnyMessageLikeEventContent {
|
|||||||
Self::Notice(ev) => ev.relates_to.clone().map(Into::into),
|
Self::Notice(ev) => ev.relates_to.clone().map(Into::into),
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
Self::Emote(ev) => ev.relates_to.clone().map(Into::into),
|
Self::Emote(ev) => ev.relates_to.clone().map(Into::into),
|
||||||
|
#[cfg(feature = "unstable-msc3246")]
|
||||||
|
Self::Audio(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")]
|
#[cfg(feature = "unstable-msc3552")]
|
||||||
|
381
crates/ruma-common/tests/events/audio.rs
Normal file
381
crates/ruma-common/tests/events/audio.rs
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
#![cfg(feature = "unstable-msc3246")]
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use assign::assign;
|
||||||
|
use js_int::uint;
|
||||||
|
use matches::assert_matches;
|
||||||
|
use ruma_common::{
|
||||||
|
event_id,
|
||||||
|
events::{
|
||||||
|
audio::{Amplitude, AudioContent, AudioEventContent, Waveform, WaveformError},
|
||||||
|
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||||
|
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 waveform_deserialization_pass() {
|
||||||
|
let json_data = json!([
|
||||||
|
13, 34, 987, 937, 345, 648, 1, 366, 235, 125, 904, 783, 734, 13, 34, 987, 937, 345, 648, 1,
|
||||||
|
366, 235, 125, 904, 783, 734, 13, 34, 987, 937, 345, 648, 1, 366, 235, 125, 904, 783, 734,
|
||||||
|
13, 34, 987, 937, 345, 648, 1, 366, 235, 125, 904, 783, 734,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<Waveform>(json_data),
|
||||||
|
Ok(waveform) if waveform.amplitudes().len() == 52
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn waveform_deserialization_not_enough() {
|
||||||
|
let json_data = json!([]);
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<Waveform>(json_data),
|
||||||
|
Err(err)
|
||||||
|
if err.is_data()
|
||||||
|
&& format!("{}", err) == format!("{}", WaveformError::NotEnoughValues)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn waveform_deserialization_clamp_amplitude() {
|
||||||
|
let json_data = json!([
|
||||||
|
2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
|
||||||
|
2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<Waveform>(json_data).unwrap(),
|
||||||
|
waveform if waveform.amplitudes().iter().all(|amp| amp.value() == Amplitude::MAX.into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_content_serialization() {
|
||||||
|
let event_content = AudioEventContent::plain(
|
||||||
|
"Upload: my_sound.ogg",
|
||||||
|
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_sound.ogg",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.audio": {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypted_content_serialization() {
|
||||||
|
let event_content = AudioEventContent::plain(
|
||||||
|
"Upload: my_sound.ogg",
|
||||||
|
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_sound.ogg",
|
||||||
|
"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.audio": {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn event_serialization() {
|
||||||
|
let event = MessageLikeEvent {
|
||||||
|
content: assign!(
|
||||||
|
AudioEventContent::with_message(
|
||||||
|
MessageContent::html(
|
||||||
|
"Upload: my_mix.mp3",
|
||||||
|
"Upload: <strong>my_mix.mp3</strong>",
|
||||||
|
),
|
||||||
|
FileContent::plain(
|
||||||
|
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||||
|
Some(Box::new(assign!(
|
||||||
|
FileContentInfo::new(),
|
||||||
|
{
|
||||||
|
name: Some("my_mix.mp3".to_owned()),
|
||||||
|
mimetype: Some("audio/mp3".to_owned()),
|
||||||
|
size: Some(uint!(897_774)),
|
||||||
|
}
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
{
|
||||||
|
audio: assign!(
|
||||||
|
AudioContent::new(),
|
||||||
|
{
|
||||||
|
duration: Some(Duration::from_secs(123))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
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_mix.mp3</strong>", "mimetype": "text/html"},
|
||||||
|
{ "body": "Upload: my_mix.mp3", "mimetype": "text/plain"},
|
||||||
|
],
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"name": "my_mix.mp3",
|
||||||
|
"mimetype": "audio/mp3",
|
||||||
|
"size": 897_774,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.audio": {
|
||||||
|
"duration": 123_000,
|
||||||
|
},
|
||||||
|
"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.audio",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_content_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_new_song.webm",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.audio": {
|
||||||
|
"waveform": [
|
||||||
|
13,
|
||||||
|
34,
|
||||||
|
987,
|
||||||
|
937,
|
||||||
|
345,
|
||||||
|
648,
|
||||||
|
1,
|
||||||
|
366,
|
||||||
|
235,
|
||||||
|
125,
|
||||||
|
904,
|
||||||
|
783,
|
||||||
|
734,
|
||||||
|
13,
|
||||||
|
34,
|
||||||
|
987,
|
||||||
|
937,
|
||||||
|
345,
|
||||||
|
648,
|
||||||
|
1,
|
||||||
|
366,
|
||||||
|
235,
|
||||||
|
125,
|
||||||
|
904,
|
||||||
|
783,
|
||||||
|
734,
|
||||||
|
13,
|
||||||
|
34,
|
||||||
|
987,
|
||||||
|
937,
|
||||||
|
345,
|
||||||
|
648,
|
||||||
|
1,
|
||||||
|
366,
|
||||||
|
235,
|
||||||
|
125,
|
||||||
|
904,
|
||||||
|
783,
|
||||||
|
734,
|
||||||
|
13,
|
||||||
|
34,
|
||||||
|
987,
|
||||||
|
937,
|
||||||
|
345,
|
||||||
|
648,
|
||||||
|
1,
|
||||||
|
366,
|
||||||
|
235,
|
||||||
|
125,
|
||||||
|
904,
|
||||||
|
783,
|
||||||
|
734,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AudioEventContent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
AudioEventContent {
|
||||||
|
message,
|
||||||
|
file,
|
||||||
|
audio: AudioContent { duration: None, waveform: Some(waveform), .. },
|
||||||
|
..
|
||||||
|
}
|
||||||
|
if message.find_plain() == Some("Upload: my_new_song.webm")
|
||||||
|
&& message.find_html().is_none()
|
||||||
|
&& file.url == "mxc://notareal.hs/abcdef"
|
||||||
|
&& waveform.amplitudes().len() == 52
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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.audio": {},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AudioEventContent>(json_data)
|
||||||
|
.unwrap(),
|
||||||
|
AudioEventContent {
|
||||||
|
message,
|
||||||
|
file,
|
||||||
|
audio: AudioContent { duration: None, waveform: None, .. },
|
||||||
|
..
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn message_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"org.matrix.msc1767.text": "Upload: airplane_sound.opus",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
|
"name": "airplane_sound.opus",
|
||||||
|
"mimetype": "audio/opus",
|
||||||
|
"size": 123_774,
|
||||||
|
},
|
||||||
|
"org.matrix.msc1767.audio": {
|
||||||
|
"duration": 5_300,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.audio",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
||||||
|
AnyMessageLikeEvent::Audio(MessageLikeEvent {
|
||||||
|
content: AudioEventContent {
|
||||||
|
message,
|
||||||
|
file: FileContent {
|
||||||
|
url,
|
||||||
|
info: Some(info),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
audio,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
event_id,
|
||||||
|
origin_server_ts,
|
||||||
|
room_id,
|
||||||
|
sender,
|
||||||
|
unsigned
|
||||||
|
}) if event_id == event_id!("$event:notareal.hs")
|
||||||
|
&& message.find_plain() == Some("Upload: airplane_sound.opus")
|
||||||
|
&& message.find_html().is_none()
|
||||||
|
&& url == "mxc://notareal.hs/abcdef"
|
||||||
|
&& info.name.as_deref() == Some("airplane_sound.opus")
|
||||||
|
&& info.mimetype.as_deref() == Some("audio/opus")
|
||||||
|
&& info.size == Some(uint!(123_774))
|
||||||
|
&& audio.duration == Some(Duration::from_millis(5_300))
|
||||||
|
&& audio.waveform.is_none()
|
||||||
|
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
||||||
|
&& room_id == room_id!("!roomid:notareal.hs")
|
||||||
|
&& sender == user_id!("@user:notareal.hs")
|
||||||
|
&& unsigned.is_empty()
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg(feature = "events")]
|
#![cfg(feature = "events")]
|
||||||
|
|
||||||
|
mod audio;
|
||||||
mod compat;
|
mod compat;
|
||||||
mod enums;
|
mod enums;
|
||||||
mod ephemeral_event;
|
mod ephemeral_event;
|
||||||
|
@ -117,6 +117,7 @@ unstable-msc2448 = [
|
|||||||
unstable-msc2675 = ["ruma-common/unstable-msc2675"]
|
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-msc3246 = ["ruma-common/unstable-msc3246"]
|
||||||
unstable-msc3551 = ["ruma-common/unstable-msc3551"]
|
unstable-msc3551 = ["ruma-common/unstable-msc3551"]
|
||||||
unstable-msc3552 = ["ruma-common/unstable-msc3552"]
|
unstable-msc3552 = ["ruma-common/unstable-msc3552"]
|
||||||
unstable-msc3553 = ["ruma-common/unstable-msc3553"]
|
unstable-msc3553 = ["ruma-common/unstable-msc3553"]
|
||||||
@ -132,6 +133,7 @@ __ci = [
|
|||||||
"unstable-msc2675",
|
"unstable-msc2675",
|
||||||
"unstable-msc2676",
|
"unstable-msc2676",
|
||||||
"unstable-msc2677",
|
"unstable-msc2677",
|
||||||
|
"unstable-msc3246",
|
||||||
"unstable-msc3551",
|
"unstable-msc3551",
|
||||||
"unstable-msc3552",
|
"unstable-msc3552",
|
||||||
"unstable-msc3553",
|
"unstable-msc3553",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user