events: Avoid unnecessary allocations during conversion

This commit is contained in:
Kévin Commaille 2022-07-02 17:34:10 +02:00 committed by Kévin Commaille
parent 96155915fe
commit bda17c3662
11 changed files with 232 additions and 165 deletions

View File

@ -89,12 +89,17 @@ impl AudioEventContent {
#[cfg(feature = "unstable-msc3245")] #[cfg(feature = "unstable-msc3245")]
voice: _, voice: _,
} = content; } = content;
let AudioInfo { duration, mimetype, size } = info.map(|info| *info).unwrap_or_default();
let message = message.unwrap_or_else(|| MessageContent::plain(body)); let message = message.unwrap_or_else(|| MessageContent::plain(body));
let file = file.unwrap_or_else(|| { let file = file.unwrap_or_else(|| {
FileContent::from_room_message_content(source, info.as_deref(), None) FileContent::from_room_message_content(source, None, mimetype, size)
});
let audio = audio.unwrap_or_else(|| {
let mut content = AudioContent::new();
content.duration = duration;
content
}); });
let audio = audio.or_else(|| info.as_deref().map(Into::into)).unwrap_or_default();
Self { message, file, audio, relates_to } Self { message, file, audio, relates_to }
} }
@ -136,19 +141,17 @@ impl AudioContent {
Self::default() Self::default()
} }
/// Creates a new `AudioContent` with the given duration.
pub(crate) fn from_room_message_content(duration: Duration) -> Self {
Self { duration: Some(duration), ..Default::default() }
}
/// Whether this `AudioContent` is empty. /// Whether this `AudioContent` is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.duration.is_none() && self.waveform.is_none() self.duration.is_none() && self.waveform.is_none()
} }
} }
impl From<&AudioInfo> for AudioContent {
fn from(info: &AudioInfo) -> Self {
let AudioInfo { duration, .. } = info;
Self { duration: duration.to_owned(), ..Default::default() }
}
}
/// The waveform representation of audio content. /// The waveform representation of audio content.
/// ///
/// Must include between 30 and 120 `Amplitude`s. /// Must include between 30 and 120 `Amplitude`s.

View File

@ -12,10 +12,9 @@ use super::{
message::MessageContent, message::MessageContent,
room::{ room::{
message::{ message::{
AudioInfo, FileInfo, FileMessageEventContent, MessageType, Relation, FileInfo, FileMessageEventContent, MessageType, Relation, RoomMessageEventContent,
RoomMessageEventContent, VideoInfo,
}, },
EncryptedFile, ImageInfo, JsonWebKey, MediaSource, EncryptedFile, JsonWebKey, MediaSource,
}, },
}; };
use crate::{serde::Base64, OwnedMxcUri}; use crate::{serde::Base64, OwnedMxcUri};
@ -108,10 +107,11 @@ impl FileEventContent {
relates_to: Option<Relation>, relates_to: Option<Relation>,
) -> Self { ) -> Self {
let FileMessageEventContent { body, filename, source, info, message, file } = content; let FileMessageEventContent { body, filename, source, info, message, file } = content;
let FileInfo { mimetype, size, .. } = info.map(|info| *info).unwrap_or_default();
let message = message.unwrap_or_else(|| MessageContent::plain(body)); let message = message.unwrap_or_else(|| MessageContent::plain(body));
let file = file.unwrap_or_else(|| { let file = file.unwrap_or_else(|| {
FileContent::from_room_message_content(source, info.as_deref(), filename) FileContent::from_room_message_content(source, filename, mimetype, size)
}); });
Self { message, file, relates_to } Self { message, file, relates_to }
@ -167,16 +167,15 @@ impl FileContent {
/// Create a new `FileContent` with the given media source, file info and filename. /// Create a new `FileContent` with the given media source, file info and filename.
pub fn from_room_message_content( pub fn from_room_message_content(
source: MediaSource, source: MediaSource,
info: Option<impl Into<FileContentInfo>>,
filename: Option<String>, filename: Option<String>,
mimetype: Option<String>,
size: Option<UInt>,
) -> Self { ) -> Self {
let (url, encryption_info) = match source { let (url, encryption_info) = source.into_extensible_content();
MediaSource::Plain(url) => (url, None), let info =
MediaSource::Encrypted(file) => (file.url.clone(), Some(Box::new((&*file).into()))), FileContentInfo::from_room_message_content(filename, mimetype, size).map(Box::new);
};
let info = FileContentInfo::from_room_message_content(info, filename).map(Box::new);
Self { url, encryption_info, info } Self { url, encryption_info: encryption_info.map(Box::new), info }
} }
/// Whether the file is encrypted. /// Whether the file is encrypted.
@ -208,51 +207,22 @@ impl FileContentInfo {
Self::default() Self::default()
} }
/// Create a new `FileContentInfo` with the given file info and filename. /// Create a new `FileContentInfo` with the given filename, mimetype and size.
/// ///
/// Returns `None` if both parameters are `None`. /// Returns `None` if all parameters are `None`.
pub fn from_room_message_content( pub fn from_room_message_content(
info: Option<impl Into<FileContentInfo>>,
filename: Option<String>, filename: Option<String>,
mimetype: Option<String>,
size: Option<UInt>,
) -> Option<Self> { ) -> Option<Self> {
if filename.is_none() && info.is_none() { if filename.is_none() && mimetype.is_none() && size.is_none() {
None None
} else { } else {
let mut info: Self = info.map(Into::into).unwrap_or_default(); Some(Self { name: filename, mimetype, size })
info.name = filename;
Some(info)
} }
} }
} }
impl From<&AudioInfo> for FileContentInfo {
fn from(info: &AudioInfo) -> Self {
let AudioInfo { mimetype, size, .. } = info;
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
}
}
impl From<&FileInfo> for FileContentInfo {
fn from(info: &FileInfo) -> Self {
let FileInfo { mimetype, size, .. } = info;
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
}
}
impl From<&ImageInfo> for FileContentInfo {
fn from(info: &ImageInfo) -> Self {
let ImageInfo { mimetype, size, .. } = info;
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
}
}
impl From<&VideoInfo> for FileContentInfo {
fn from(info: &VideoInfo) -> Self {
let VideoInfo { mimetype, size, .. } = info;
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
}
}
/// The encryption info of a file sent to a room with end-to-end encryption enabled. /// The encryption info of a file sent to a room with end-to-end encryption enabled.
/// ///
/// To create an instance of this type, first create a `EncryptedContentInit` and convert it via /// To create an instance of this type, first create a `EncryptedContentInit` and convert it via
@ -310,7 +280,7 @@ impl From<EncryptedContentInit> for EncryptedContent {
impl From<&EncryptedFile> for EncryptedContent { impl From<&EncryptedFile> for EncryptedContent {
fn from(encrypted: &EncryptedFile) -> Self { fn from(encrypted: &EncryptedFile) -> Self {
let EncryptedFile { key, iv, hashes, v, .. } = encrypted.to_owned(); let EncryptedFile { key, iv, hashes, v, .. } = encrypted;
Self { key, iv, hashes, v } Self { key: key.to_owned(), iv: iv.to_owned(), hashes: hashes.to_owned(), v: v.to_owned() }
} }
} }

View File

@ -104,25 +104,21 @@ impl ImageEventContent {
thumbnail, thumbnail,
caption, caption,
} = content; } = content;
let ImageInfo { height, width, mimetype, size, thumbnail_info, thumbnail_source, .. } =
info.map(|info| *info).unwrap_or_default();
let message = message.unwrap_or_else(|| MessageContent::plain(body)); let message = message.unwrap_or_else(|| MessageContent::plain(body));
let file = file.unwrap_or_else(|| { let file = file.unwrap_or_else(|| {
FileContent::from_room_message_content(source, info.as_deref(), None) FileContent::from_room_message_content(source, None, mimetype, size)
}); });
let image = let image = image
image.or_else(|| info.as_deref().map(|info| Box::new(info.into()))).unwrap_or_default(); .or_else(|| ImageContent::from_room_message_content(width, height).map(Box::new))
let thumbnail = thumbnail
.or_else(|| {
info.as_deref()
.and_then(|info| {
ThumbnailContent::from_room_message_content(
info.thumbnail_source.as_ref(),
info.thumbnail_info.as_deref(),
)
})
.map(|thumbnail| vec![thumbnail])
})
.unwrap_or_default(); .unwrap_or_default();
let thumbnail = thumbnail.unwrap_or_else(|| {
ThumbnailContent::from_room_message_content(thumbnail_source, thumbnail_info)
.into_iter()
.collect()
});
Self { message, file, image, thumbnail, caption, relates_to } Self { message, file, image, thumbnail, caption, relates_to }
} }
@ -165,26 +161,26 @@ impl ImageContent {
Self { height: Some(height), width: Some(width) } Self { height: Some(height), width: Some(width) }
} }
/// Creates a new `ImageContent` with the given optional width and height.
///
/// Returns `None` if both parameters are `None`.
pub(crate) fn from_room_message_content(
width: Option<UInt>,
height: Option<UInt>,
) -> Option<Self> {
if width.is_none() && height.is_none() {
None
} else {
Some(Self { width, height })
}
}
/// Whether this `ImageContent` is empty. /// Whether this `ImageContent` is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.height.is_none() && self.width.is_none() self.height.is_none() && self.width.is_none()
} }
} }
impl From<&ImageInfo> for ImageContent {
fn from(info: &ImageInfo) -> Self {
let ImageInfo { height, width, .. } = info;
Self { height: height.to_owned(), width: width.to_owned() }
}
}
impl From<&ThumbnailInfo> for ImageContent {
fn from(info: &ThumbnailInfo) -> Self {
let ThumbnailInfo { height, width, .. } = info;
Self { height: height.to_owned(), width: width.to_owned() }
}
}
/// Thumbnail content. /// Thumbnail content.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
@ -208,13 +204,14 @@ impl ThumbnailContent {
/// ///
/// Returns `None` if no thumbnail was found. /// Returns `None` if no thumbnail was found.
pub fn from_room_message_content( pub fn from_room_message_content(
thumbnail_source: Option<&MediaSource>, source: Option<MediaSource>,
thumbnail_info: Option<&ThumbnailInfo>, info: Option<Box<ThumbnailInfo>>,
) -> Option<Self> { ) -> Option<Self> {
thumbnail_source.map(|thumbnail_source| { source.map(|source| {
let file = let ThumbnailInfo { height, width, mimetype, size } = *info.unwrap_or_default();
ThumbnailFileContent::from_room_message_content(thumbnail_source, thumbnail_info);
let image = thumbnail_info.map(|info| Box::new(info.into())); let file = ThumbnailFileContent::from_room_message_content(source, mimetype, size);
let image = ImageContent::from_room_message_content(width, height).map(Box::new);
Self { file, image } Self { file, image }
}) })
@ -259,18 +256,15 @@ impl ThumbnailFileContent {
/// ///
/// Returns `None` if no thumbnail was found. /// Returns `None` if no thumbnail was found.
fn from_room_message_content( fn from_room_message_content(
thumbnail_source: &MediaSource, source: MediaSource,
thumbnail_info: Option<&ThumbnailInfo>, mimetype: Option<String>,
size: Option<UInt>,
) -> Self { ) -> Self {
match thumbnail_source { let info =
MediaSource::Plain(url) => { ThumbnailFileContentInfo::from_room_message_content(mimetype, size).map(Box::new);
Self::plain(url.to_owned(), thumbnail_info.map(|info| Box::new(info.into()))) match source.into_extensible_content() {
} (url, None) => Self::plain(url, info),
MediaSource::Encrypted(file) => Self::encrypted( (url, Some(encryption_info)) => Self::encrypted(url, encryption_info, info),
file.url.clone(),
(&**file).into(),
thumbnail_info.map(|info| Box::new(info.into())),
),
} }
} }
@ -298,11 +292,15 @@ impl ThumbnailFileContentInfo {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
}
impl From<&ThumbnailInfo> for ThumbnailFileContentInfo { /// Creates a new `ThumbnailFileContentInfo` with the given optional MIME type and size.
fn from(info: &ThumbnailInfo) -> Self { ///
let ThumbnailInfo { mimetype, size, .. } = info; /// Returns `None` if both the mimetype and the size are `None`.
Self { mimetype: mimetype.to_owned(), size: size.to_owned() } fn from_room_message_content(mimetype: Option<String>, size: Option<UInt>) -> Option<Self> {
if mimetype.is_none() && size.is_none() {
None
} else {
Some(Self { mimetype, size })
}
} }
} }

View File

@ -8,7 +8,7 @@ use js_int::UInt;
use serde::{de, Deserialize, Serialize}; use serde::{de, Deserialize, Serialize};
#[cfg(feature = "unstable-msc3551")] #[cfg(feature = "unstable-msc3551")]
use super::file::{EncryptedContent, FileContent}; use super::file::{EncryptedContent, EncryptedContentInit, FileContent};
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
use super::{ use super::{
file::FileContentInfo, file::FileContentInfo,
@ -55,6 +55,19 @@ pub enum MediaSource {
Encrypted(Box<EncryptedFile>), Encrypted(Box<EncryptedFile>),
} }
#[cfg(feature = "unstable-msc3551")]
impl MediaSource {
pub(crate) fn into_extensible_content(self) -> (OwnedMxcUri, Option<EncryptedContent>) {
match self {
MediaSource::Plain(url) => (url, None),
MediaSource::Encrypted(encrypted_file) => {
let EncryptedFile { url, key, iv, hashes, v } = *encrypted_file;
(url, Some(EncryptedContentInit { key, iv, hashes, v }.into()))
}
}
}
}
// Custom implementation of `Deserialize`, because serde doesn't guarantee what variant will be // Custom implementation of `Deserialize`, because serde doesn't guarantee what variant will be
// deserialized for "externally tagged"¹ enums where multiple "tag" fields exist. // deserialized for "externally tagged"¹ enums where multiple "tag" fields exist.
// //

View File

@ -8,7 +8,7 @@ use crate::events::voice::VoiceContent;
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
use crate::events::{ use crate::events::{
audio::AudioContent, audio::AudioContent,
file::{FileContent, FileContentInfo}, file::{EncryptedContent, FileContent, FileContentInfo},
message::MessageContent, message::MessageContent,
}; };
use crate::{ use crate::{
@ -84,10 +84,22 @@ impl AudioMessageEventContent {
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
file: Some(FileContent::plain( file: Some(FileContent::plain(
url.clone(), url.clone(),
info.as_deref().map(|info| Box::new(info.into())), info.as_deref().and_then(|info| {
FileContentInfo::from_room_message_content(
None,
info.mimetype.to_owned(),
info.size.to_owned(),
)
.map(Box::new)
}),
)), )),
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
audio: Some(info.as_deref().map_or_else(AudioContent::default, Into::into)), audio: Some(
info.as_deref()
.and_then(|info| info.duration)
.map(AudioContent::from_room_message_content)
.unwrap_or_default(),
),
#[cfg(feature = "unstable-msc3245")] #[cfg(feature = "unstable-msc3245")]
voice: None, voice: None,
body, body,
@ -103,7 +115,11 @@ impl AudioMessageEventContent {
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
message: Some(MessageContent::plain(body.clone())), message: Some(MessageContent::plain(body.clone())),
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)), file: Some(FileContent::encrypted(
file.url.clone(),
EncryptedContent::from(&file),
None,
)),
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
audio: Some(AudioContent::default()), audio: Some(AudioContent::default()),
#[cfg(feature = "unstable-msc3245")] #[cfg(feature = "unstable-msc3245")]
@ -184,19 +200,24 @@ impl AudioInfo {
} }
/// Create an `AudioInfo` from the given file info and audio info. /// Create an `AudioInfo` from the given file info and audio info.
///
/// Returns `None` if the `AudioInfo` would be empty.
#[cfg(feature = "unstable-msc3246")] #[cfg(feature = "unstable-msc3246")]
pub fn from_extensible_content( pub fn from_extensible_content(
file_info: Option<&FileContentInfo>, file_info: Option<&FileContentInfo>,
audio: &AudioContent, audio: &AudioContent,
) -> Option<Self> { ) -> Option<Self> {
if file_info.is_none() && audio.is_empty() {
None
} else {
let (mimetype, size) = file_info let (mimetype, size) = file_info
.map(|info| (info.mimetype.to_owned(), info.size.to_owned())) .map(|info| {
let FileContentInfo { mimetype, size, .. } = info;
(mimetype.to_owned(), size.to_owned())
})
.unwrap_or_default(); .unwrap_or_default();
let AudioContent { duration, .. } = audio; let AudioContent { duration, .. } = audio;
if duration.is_none() && mimetype.is_none() && size.is_none() {
None
} else {
Some(Self { duration: duration.to_owned(), mimetype, size }) Some(Self { duration: duration.to_owned(), mimetype, size })
} }
} }

View File

@ -68,7 +68,14 @@ impl FileMessageEventContent {
#[cfg(feature = "unstable-msc3551")] #[cfg(feature = "unstable-msc3551")]
file: Some(FileContent::plain( file: Some(FileContent::plain(
url.clone(), url.clone(),
info.as_deref().map(|info| Box::new(info.into())), info.as_deref().and_then(|info| {
FileContentInfo::from_room_message_content(
None,
info.mimetype.to_owned(),
info.size.to_owned(),
)
.map(Box::new)
}),
)), )),
body, body,
filename: None, filename: None,
@ -101,7 +108,10 @@ impl FileMessageEventContent {
message[0].body.clone() message[0].body.clone()
}; };
let filename = file.info.as_deref().and_then(|info| info.name.clone()); let filename = file.info.as_deref().and_then(|info| info.name.clone());
let info = file.info.as_deref().map(|info| Box::new(info.into())); let info = file.info.as_deref().and_then(|info| {
FileInfo::from_extensible_content(info.mimetype.to_owned(), info.size.to_owned())
.map(Box::new)
});
let source = (&file).into(); let source = (&file).into();
Self { message: Some(message), file: Some(file), body, filename, source, info } Self { message: Some(message), file: Some(file), body, filename, source, info }
@ -138,12 +148,16 @@ impl FileInfo {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
}
/// Creates a `FileInfo` with the given optional mimetype and size.
///
/// Returns `None` if the `FileInfo` would be empty.
#[cfg(feature = "unstable-msc3551")] #[cfg(feature = "unstable-msc3551")]
impl From<&FileContentInfo> for FileInfo { fn from_extensible_content(mimetype: Option<String>, size: Option<UInt>) -> Option<Self> {
fn from(info: &FileContentInfo) -> Self { if mimetype.is_none() && size.is_none() {
let FileContentInfo { mimetype, size, .. } = info; None
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() } } else {
Some(Self { mimetype, size, ..Default::default() })
}
} }
} }

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
use crate::events::{ use crate::events::{
file::FileContent, file::{FileContent, FileContentInfo},
image::{ImageContent, ThumbnailContent}, image::{ImageContent, ThumbnailContent},
message::MessageContent, message::MessageContent,
}; };
@ -89,17 +89,30 @@ impl ImageMessageEventContent {
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
file: Some(FileContent::plain( file: Some(FileContent::plain(
url.clone(), url.clone(),
info.as_deref().map(|info| Box::new(info.into())), info.as_deref().and_then(|info| {
FileContentInfo::from_room_message_content(
None,
info.mimetype.to_owned(),
info.size,
)
.map(Box::new)
}),
)), )),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
image: Some(Box::new(info.as_deref().map_or_else(ImageContent::default, Into::into))), image: Some(Box::new(
info.as_deref()
.and_then(|info| {
ImageContent::from_room_message_content(info.width, info.height)
})
.unwrap_or_default(),
)),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
thumbnail: info thumbnail: info
.as_deref() .as_deref()
.and_then(|info| { .and_then(|info| {
ThumbnailContent::from_room_message_content( ThumbnailContent::from_room_message_content(
info.thumbnail_source.as_ref(), info.thumbnail_source.to_owned(),
info.thumbnail_info.as_deref(), info.thumbnail_info.to_owned(),
) )
}) })
.map(|thumbnail| vec![thumbnail]), .map(|thumbnail| vec![thumbnail]),

View File

@ -91,17 +91,34 @@ impl VideoMessageEventContent {
#[cfg(feature = "unstable-msc3553")] #[cfg(feature = "unstable-msc3553")]
file: Some(FileContent::plain( file: Some(FileContent::plain(
url.clone(), url.clone(),
info.as_deref().map(|info| Box::new(info.into())), info.as_deref().and_then(|info| {
FileContentInfo::from_room_message_content(
None,
info.mimetype.to_owned(),
info.size.to_owned(),
)
.map(Box::new)
}),
)), )),
#[cfg(feature = "unstable-msc3553")] #[cfg(feature = "unstable-msc3553")]
video: Some(Box::new(info.as_deref().map_or_else(VideoContent::default, Into::into))), video: Some(Box::new(
info.as_deref()
.map(|info| {
VideoContent::from_room_message_content(
info.height,
info.width,
info.duration,
)
})
.unwrap_or_default(),
)),
#[cfg(feature = "unstable-msc3553")] #[cfg(feature = "unstable-msc3553")]
thumbnail: info thumbnail: info
.as_deref() .as_deref()
.and_then(|info| { .and_then(|info| {
ThumbnailContent::from_room_message_content( ThumbnailContent::from_room_message_content(
info.thumbnail_source.as_ref(), info.thumbnail_source.to_owned(),
info.thumbnail_info.as_deref(), info.thumbnail_info.to_owned(),
) )
}) })
.map(|thumbnail| vec![thumbnail]), .map(|thumbnail| vec![thumbnail]),

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
use super::{ use super::{
file::FileContent, file::{FileContent, FileContentInfo},
image::{ImageContent, ThumbnailContent}, image::{ImageContent, ThumbnailContent},
message::MessageContent, message::MessageContent,
}; };
@ -96,13 +96,20 @@ impl StickerEventContent {
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
message: Some(MessageContent::plain(body.clone())), message: Some(MessageContent::plain(body.clone())),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
file: Some(FileContent::plain(url.clone(), Some(Box::new((&info).into())))), file: Some(FileContent::plain(
url.clone(),
FileContentInfo::from_room_message_content(None, info.mimetype.clone(), info.size)
.map(Box::new),
)),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
image: Some(Box::new((&info).into())), image: Some(Box::new(
ImageContent::from_room_message_content(info.width, info.height)
.unwrap_or_default(),
)),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]
thumbnail: ThumbnailContent::from_room_message_content( thumbnail: ThumbnailContent::from_room_message_content(
info.thumbnail_source.as_ref(), info.thumbnail_source.clone(),
info.thumbnail_info.as_deref(), info.thumbnail_info.clone(),
) )
.map(|thumbnail| vec![thumbnail]), .map(|thumbnail| vec![thumbnail]),
#[cfg(feature = "unstable-msc3552")] #[cfg(feature = "unstable-msc3552")]

View File

@ -105,25 +105,29 @@ impl VideoEventContent {
thumbnail, thumbnail,
caption, caption,
} = content; } = content;
let VideoInfo {
duration,
height,
width,
mimetype,
size,
thumbnail_info,
thumbnail_source,
..
} = info.map(|info| *info).unwrap_or_default();
let message = message.unwrap_or_else(|| MessageContent::plain(body)); let message = message.unwrap_or_else(|| MessageContent::plain(body));
let file = file.unwrap_or_else(|| { let file = file.unwrap_or_else(|| {
FileContent::from_room_message_content(source, info.as_deref(), None) FileContent::from_room_message_content(source, None, mimetype, size)
});
let video = video.unwrap_or_else(|| {
Box::new(VideoContent::from_room_message_content(height, width, duration))
});
let thumbnail = thumbnail.unwrap_or_else(|| {
ThumbnailContent::from_room_message_content(thumbnail_source, thumbnail_info)
.into_iter()
.collect()
}); });
let video =
video.or_else(|| info.as_deref().map(|info| Box::new(info.into()))).unwrap_or_default();
let thumbnail = thumbnail
.or_else(|| {
info.as_deref()
.and_then(|info| {
ThumbnailContent::from_room_message_content(
info.thumbnail_source.as_ref(),
info.thumbnail_info.as_deref(),
)
})
.map(|thumbnail| vec![thumbnail])
})
.unwrap_or_default();
Self { message, file, video, thumbnail, caption, relates_to } Self { message, file, video, thumbnail, caption, relates_to }
} }
@ -169,15 +173,17 @@ impl VideoContent {
Self::default() Self::default()
} }
/// Creates a new `VideoContent` with the given optional height, width and duration.
pub(crate) fn from_room_message_content(
height: Option<UInt>,
width: Option<UInt>,
duration: Option<Duration>,
) -> Self {
Self { height, width, duration }
}
/// Whether this `VideoContent` is empty. /// Whether this `VideoContent` is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.height.is_none() && self.width.is_none() && self.duration.is_none() self.height.is_none() && self.width.is_none() && self.duration.is_none()
} }
} }
impl From<&VideoInfo> for VideoContent {
fn from(info: &VideoInfo) -> Self {
let VideoInfo { height, width, duration, .. } = info;
Self { height: height.to_owned(), width: width.to_owned(), duration: duration.to_owned() }
}
}

View File

@ -9,7 +9,9 @@ use super::{
audio::AudioContent, audio::AudioContent,
file::FileContent, file::FileContent,
message::{MessageContent, TryFromExtensibleError}, message::{MessageContent, TryFromExtensibleError},
room::message::{AudioMessageEventContent, MessageType, Relation, RoomMessageEventContent}, room::message::{
AudioInfo, AudioMessageEventContent, MessageType, Relation, RoomMessageEventContent,
},
}; };
/// The payload for an extensible voice message. /// The payload for an extensible voice message.
@ -82,12 +84,15 @@ impl VoiceEventContent {
relates_to: Option<Relation>, relates_to: Option<Relation>,
) -> Result<Self, TryFromExtensibleError> { ) -> Result<Self, TryFromExtensibleError> {
let AudioMessageEventContent { body, source, info, message, file, audio, voice } = content; let AudioMessageEventContent { body, source, info, message, file, audio, voice } = content;
let AudioInfo { duration, mimetype, size } = info.map(|info| *info).unwrap_or_default();
let message = message.unwrap_or_else(|| MessageContent::plain(body)); let message = message.unwrap_or_else(|| MessageContent::plain(body));
let file = file.unwrap_or_else(|| { let file = file.unwrap_or_else(|| {
FileContent::from_room_message_content(source, info.as_deref(), None) FileContent::from_room_message_content(source, None, mimetype, size)
}); });
let audio = audio.or_else(|| info.as_deref().map(Into::into)).unwrap_or_default(); let audio = audio
.or_else(|| duration.map(AudioContent::from_room_message_content))
.unwrap_or_default();
let voice = if let Some(voice) = voice { let voice = if let Some(voice) = voice {
voice voice
} else { } else {