matrix_sdk/attachment.rs
1// Copyright 2022 Kévin Commaille
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types and traits for attachments.
16
17use std::time::Duration;
18
19use ruma::{
20 OwnedTransactionId, UInt, assign,
21 events::{
22 Mentions,
23 room::{
24 ImageInfo, ThumbnailInfo,
25 message::{AudioInfo, FileInfo, TextMessageEventContent, VideoInfo},
26 },
27 },
28};
29
30use crate::room::reply::Reply;
31
32/// Base metadata about an image.
33#[derive(Debug, Clone, Default)]
34pub struct BaseImageInfo {
35 /// The height of the image in pixels.
36 pub height: Option<UInt>,
37 /// The width of the image in pixels.
38 pub width: Option<UInt>,
39 /// The file size of the image in bytes.
40 pub size: Option<UInt>,
41 /// The [BlurHash](https://blurha.sh/) for this image.
42 pub blurhash: Option<String>,
43 /// Whether this image is animated.
44 pub is_animated: Option<bool>,
45}
46
47/// Base metadata about a video.
48#[derive(Debug, Clone, Default)]
49pub struct BaseVideoInfo {
50 /// The duration of the video.
51 pub duration: Option<Duration>,
52 /// The height of the video in pixels.
53 pub height: Option<UInt>,
54 /// The width of the video in pixels.
55 pub width: Option<UInt>,
56 /// The file size of the video in bytes.
57 pub size: Option<UInt>,
58 /// The [BlurHash](https://blurha.sh/) for this video.
59 pub blurhash: Option<String>,
60 /// Whether this video should be displayed as a circle video
61 /// ([MSC4546](https://github.com/matrix-org/matrix-spec-proposals/pull/4546)).
62 ///
63 /// Defaults to `false`.
64 #[cfg(feature = "unstable-msc4546")]
65 pub circle: bool,
66}
67
68/// Base metadata about an audio clip.
69#[derive(Debug, Clone, Default)]
70pub struct BaseAudioInfo {
71 /// The duration of the audio clip.
72 pub duration: Option<Duration>,
73 /// The file size of the audio clip in bytes.
74 pub size: Option<UInt>,
75 /// The waveform of the audio clip.
76 ///
77 /// Must only include values between 0 and 1.
78 pub waveform: Option<Vec<f32>>,
79}
80
81/// Base metadata about a file.
82#[derive(Debug, Clone, Default)]
83pub struct BaseFileInfo {
84 /// The size of the file in bytes.
85 pub size: Option<UInt>,
86}
87
88/// Types of metadata for an attachment.
89#[derive(Debug)]
90pub enum AttachmentInfo {
91 /// The metadata of an image.
92 Image(BaseImageInfo),
93 /// The metadata of a video.
94 Video(BaseVideoInfo),
95 /// The metadata of an audio clip.
96 Audio(BaseAudioInfo),
97 /// The metadata of a file.
98 File(BaseFileInfo),
99 /// The metadata of a voice message
100 Voice(BaseAudioInfo),
101}
102
103impl From<AttachmentInfo> for ImageInfo {
104 fn from(info: AttachmentInfo) -> Self {
105 match info {
106 AttachmentInfo::Image(info) => assign!(ImageInfo::new(), {
107 height: info.height,
108 width: info.width,
109 size: info.size,
110 blurhash: info.blurhash,
111 is_animated: info.is_animated,
112 }),
113 _ => ImageInfo::new(),
114 }
115 }
116}
117
118impl From<AttachmentInfo> for VideoInfo {
119 fn from(info: AttachmentInfo) -> Self {
120 match info {
121 AttachmentInfo::Video(info) => assign!(VideoInfo::new(), {
122 duration: info.duration,
123 height: info.height,
124 width: info.width,
125 size: info.size,
126 blurhash: info.blurhash,
127 }),
128 _ => VideoInfo::new(),
129 }
130 }
131}
132
133impl From<AttachmentInfo> for AudioInfo {
134 fn from(info: AttachmentInfo) -> Self {
135 match info {
136 AttachmentInfo::Audio(info) | AttachmentInfo::Voice(info) => {
137 assign!(AudioInfo::new(), {
138 duration: info.duration,
139 size: info.size,
140 })
141 }
142 _ => AudioInfo::new(),
143 }
144 }
145}
146
147impl From<AttachmentInfo> for FileInfo {
148 fn from(info: AttachmentInfo) -> Self {
149 match info {
150 AttachmentInfo::File(info) => assign!(FileInfo::new(), {
151 size: info.size,
152 }),
153 _ => FileInfo::new(),
154 }
155 }
156}
157
158/// A thumbnail to upload and send for an attachment.
159#[derive(Debug)]
160pub struct Thumbnail {
161 /// The raw bytes of the thumbnail.
162 pub data: Vec<u8>,
163 /// The type of the thumbnail, this will be used as the content-type header.
164 pub content_type: mime::Mime,
165 /// The height of the thumbnail in pixels.
166 pub height: UInt,
167 /// The width of the thumbnail in pixels.
168 pub width: UInt,
169 /// The file size of the thumbnail in bytes.
170 pub size: UInt,
171}
172
173impl Thumbnail {
174 /// Convert this `Thumbnail` into a `(data, content_type, info)` tuple.
175 pub fn into_parts(self) -> (Vec<u8>, mime::Mime, Box<ThumbnailInfo>) {
176 let thumbnail_info = assign!(ThumbnailInfo::new(), {
177 height: Some(self.height),
178 width: Some(self.width),
179 size: Some(self.size),
180 mimetype: Some(self.content_type.to_string())
181 });
182 (self.data, self.content_type, Box::new(thumbnail_info))
183 }
184}
185
186/// Configuration for sending an attachment.
187#[derive(Debug, Default)]
188pub struct AttachmentConfig {
189 /// A fixed transaction id to be used for sending this attachment.
190 ///
191 /// Otherwise, a random one will be generated.
192 pub txn_id: Option<OwnedTransactionId>,
193
194 /// Type-specific metadata about the attachment.
195 pub info: Option<AttachmentInfo>,
196
197 /// An optional thumbnail to send with the attachment.
198 pub thumbnail: Option<Thumbnail>,
199
200 /// An optional caption for the attachment.
201 pub caption: Option<TextMessageEventContent>,
202
203 /// Intentional mentions to be included in the media event.
204 pub mentions: Option<Mentions>,
205
206 /// Reply parameters for the attachment (replied-to event and thread-related
207 /// metadata).
208 pub reply: Option<Reply>,
209
210 /// Additional fields to include in the media event's content. Objects such
211 /// as `info` are merged recursively. The event's own fields take precedence
212 /// on conflicts.
213 pub extra_content: Option<serde_json::Map<String, serde_json::Value>>,
214}
215
216impl AttachmentConfig {
217 /// Create a new empty `AttachmentConfig`.
218 pub fn new() -> Self {
219 Self::default()
220 }
221
222 /// Set the thumbnail to send.
223 ///
224 /// # Arguments
225 ///
226 /// - `thumbnail` - The thumbnail of the media. If the `content_type` does
227 /// not support it (e.g. audio clips), it is ignored.
228 #[must_use]
229 pub fn thumbnail(mut self, thumbnail: Option<Thumbnail>) -> Self {
230 self.thumbnail = thumbnail;
231 self
232 }
233
234 /// Set the transaction ID to send.
235 ///
236 /// # Arguments
237 ///
238 /// - `txn_id` - A unique ID that can be attached to a `MessageEvent` held
239 /// in its unsigned field as `transaction_id`. If not given, one is
240 /// created for the message.
241 #[must_use]
242 pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
243 self.txn_id = Some(txn_id);
244 self
245 }
246
247 /// Set the media metadata to send.
248 ///
249 /// # Arguments
250 ///
251 /// - `info` - The metadata of the media. If the `AttachmentInfo` type
252 /// doesn't match the `content_type`, it is ignored.
253 #[must_use]
254 pub fn info(mut self, info: AttachmentInfo) -> Self {
255 self.info = Some(info);
256 self
257 }
258
259 /// Set the optional caption.
260 ///
261 /// # Arguments
262 ///
263 /// * `caption` - The optional caption.
264 pub fn caption(mut self, caption: Option<TextMessageEventContent>) -> Self {
265 self.caption = caption;
266 self
267 }
268
269 /// Set the mentions of the message.
270 ///
271 /// # Arguments
272 ///
273 /// * `mentions` - The mentions of the message.
274 pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
275 self.mentions = mentions;
276 self
277 }
278
279 /// Set the reply information of the message.
280 ///
281 /// # Arguments
282 ///
283 /// * `reply` - The reply information of the message.
284 pub fn reply(mut self, reply: Option<Reply>) -> Self {
285 self.reply = reply;
286 self
287 }
288
289 /// Set additional fields for the media event's content.
290 ///
291 /// # Arguments
292 ///
293 /// * `extra_content` - The additional fields.
294 pub fn extra_content(
295 mut self,
296 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
297 ) -> Self {
298 self.extra_content = extra_content;
299 self
300 }
301}
302
303/// Configuration for sending a gallery.
304#[cfg(feature = "unstable-msc4274")]
305#[derive(Debug, Default)]
306pub struct GalleryConfig {
307 pub(crate) txn_id: Option<OwnedTransactionId>,
308 pub(crate) items: Vec<GalleryItemInfo>,
309 pub(crate) caption: Option<TextMessageEventContent>,
310 pub(crate) mentions: Option<Mentions>,
311 pub(crate) reply: Option<Reply>,
312 pub(crate) extra_content: Option<serde_json::Map<String, serde_json::Value>>,
313}
314
315#[cfg(feature = "unstable-msc4274")]
316impl GalleryConfig {
317 /// Create a new empty `GalleryConfig`.
318 pub fn new() -> Self {
319 Self::default()
320 }
321
322 /// Set the transaction ID to send.
323 ///
324 /// # Arguments
325 ///
326 /// - `txn_id` - A unique ID that can be attached to a `MessageEvent` held
327 /// in its unsigned field as `transaction_id`. If not given, one is
328 /// created for the message.
329 #[must_use]
330 pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
331 self.txn_id = Some(txn_id);
332 self
333 }
334
335 /// Adds a media item to the gallery.
336 ///
337 /// # Arguments
338 ///
339 /// * `item` - Information about the item to be added.
340 #[must_use]
341 pub fn add_item(mut self, item: GalleryItemInfo) -> Self {
342 self.items.push(item);
343 self
344 }
345
346 /// Set the optional caption.
347 ///
348 /// # Arguments
349 ///
350 /// * `caption` - The optional caption.
351 pub fn caption(mut self, caption: Option<TextMessageEventContent>) -> Self {
352 self.caption = caption;
353 self
354 }
355
356 /// Set the mentions of the message.
357 ///
358 /// # Arguments
359 ///
360 /// * `mentions` - The mentions of the message.
361 pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
362 self.mentions = mentions;
363 self
364 }
365
366 /// Set the reply information of the message.
367 ///
368 /// # Arguments
369 ///
370 /// * `reply` - The reply information of the message.
371 pub fn reply(mut self, reply: Option<Reply>) -> Self {
372 self.reply = reply;
373 self
374 }
375
376 /// Set additional top-level fields for the gallery event's content.
377 ///
378 /// Objects are merged recursively; the event's own fields take precedence
379 /// on conflicts. To add fields to individual items, use
380 /// [`GalleryItemInfo::extra_content`].
381 #[must_use]
382 pub fn extra_content(
383 mut self,
384 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
385 ) -> Self {
386 self.extra_content = extra_content;
387 self
388 }
389
390 /// Returns the number of media items in the gallery.
391 pub fn len(&self) -> usize {
392 self.items.len()
393 }
394
395 /// Checks whether the gallery contains any media items or not.
396 pub fn is_empty(&self) -> bool {
397 self.items.is_empty()
398 }
399}
400
401#[cfg(feature = "unstable-msc4274")]
402#[derive(Debug)]
403/// Metadata for a gallery item
404pub struct GalleryItemInfo {
405 /// The filename.
406 pub filename: String,
407 /// The mime type.
408 pub content_type: mime::Mime,
409 /// The binary data.
410 pub data: Vec<u8>,
411 /// The attachment info.
412 pub attachment_info: AttachmentInfo,
413 /// The caption.
414 pub caption: Option<TextMessageEventContent>,
415 /// The thumbnail.
416 pub thumbnail: Option<Thumbnail>,
417 /// Additional fields to merge into this item's content, for example a
418 /// spoiler flag. Objects are merged recursively; the item's own fields take
419 /// precedence on conflicts.
420 pub extra_content: Option<serde_json::Map<String, serde_json::Value>>,
421}