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, FormattedBody, 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}
61
62/// Base metadata about an audio clip.
63#[derive(Debug, Clone, Default)]
64pub struct BaseAudioInfo {
65 /// The duration of the audio clip.
66 pub duration: Option<Duration>,
67 /// The file size of the audio clip in bytes.
68 pub size: Option<UInt>,
69}
70
71/// Base metadata about a file.
72#[derive(Debug, Clone, Default)]
73pub struct BaseFileInfo {
74 /// The size of the file in bytes.
75 pub size: Option<UInt>,
76}
77
78/// Types of metadata for an attachment.
79#[derive(Debug)]
80pub enum AttachmentInfo {
81 /// The metadata of an image.
82 Image(BaseImageInfo),
83 /// The metadata of a video.
84 Video(BaseVideoInfo),
85 /// The metadata of an audio clip.
86 Audio(BaseAudioInfo),
87 /// The metadata of a file.
88 File(BaseFileInfo),
89 /// The metadata of a voice message
90 Voice {
91 /// The audio info
92 audio_info: BaseAudioInfo,
93 /// The waveform of the voice message
94 waveform: Option<Vec<u16>>,
95 },
96}
97
98impl From<AttachmentInfo> for ImageInfo {
99 fn from(info: AttachmentInfo) -> Self {
100 match info {
101 AttachmentInfo::Image(info) => assign!(ImageInfo::new(), {
102 height: info.height,
103 width: info.width,
104 size: info.size,
105 blurhash: info.blurhash,
106 is_animated: info.is_animated,
107 }),
108 _ => ImageInfo::new(),
109 }
110 }
111}
112
113impl From<AttachmentInfo> for VideoInfo {
114 fn from(info: AttachmentInfo) -> Self {
115 match info {
116 AttachmentInfo::Video(info) => assign!(VideoInfo::new(), {
117 duration: info.duration,
118 height: info.height,
119 width: info.width,
120 size: info.size,
121 blurhash: info.blurhash,
122 }),
123 _ => VideoInfo::new(),
124 }
125 }
126}
127
128impl From<AttachmentInfo> for AudioInfo {
129 fn from(info: AttachmentInfo) -> Self {
130 match info {
131 AttachmentInfo::Audio(info) => assign!(AudioInfo::new(), {
132 duration: info.duration,
133 size: info.size,
134 }),
135 AttachmentInfo::Voice { audio_info, .. } => assign!(AudioInfo::new(), {
136 duration: audio_info.duration,
137 size: audio_info.size,
138 }),
139 _ => AudioInfo::new(),
140 }
141 }
142}
143
144impl From<AttachmentInfo> for FileInfo {
145 fn from(info: AttachmentInfo) -> Self {
146 match info {
147 AttachmentInfo::File(info) => assign!(FileInfo::new(), {
148 size: info.size,
149 }),
150 _ => FileInfo::new(),
151 }
152 }
153}
154
155/// A thumbnail to upload and send for an attachment.
156#[derive(Debug)]
157pub struct Thumbnail {
158 /// The raw bytes of the thumbnail.
159 pub data: Vec<u8>,
160 /// The type of the thumbnail, this will be used as the content-type header.
161 pub content_type: mime::Mime,
162 /// The height of the thumbnail in pixels.
163 pub height: UInt,
164 /// The width of the thumbnail in pixels.
165 pub width: UInt,
166 /// The file size of the thumbnail in bytes.
167 pub size: UInt,
168}
169
170impl Thumbnail {
171 /// Convert this `Thumbnail` into a `(data, content_type, info)` tuple.
172 pub fn into_parts(self) -> (Vec<u8>, mime::Mime, Box<ThumbnailInfo>) {
173 let thumbnail_info = assign!(ThumbnailInfo::new(), {
174 height: Some(self.height),
175 width: Some(self.width),
176 size: Some(self.size),
177 mimetype: Some(self.content_type.to_string())
178 });
179 (self.data, self.content_type, Box::new(thumbnail_info))
180 }
181}
182
183/// Configuration for sending an attachment.
184#[derive(Debug, Default)]
185pub struct AttachmentConfig {
186 /// A fixed transaction id to be used for sending this attachment.
187 ///
188 /// Otherwise, a random one will be generated.
189 pub txn_id: Option<OwnedTransactionId>,
190
191 /// Type-specific metadata about the attachment.
192 pub info: Option<AttachmentInfo>,
193
194 /// An optional thumbnail to send with the attachment.
195 pub thumbnail: Option<Thumbnail>,
196
197 /// An optional caption for the attachment.
198 pub caption: Option<String>,
199
200 /// An optional formatted caption for the attachment.
201 pub formatted_caption: Option<FormattedBody>,
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
211impl AttachmentConfig {
212 /// Create a new empty `AttachmentConfig`.
213 pub fn new() -> Self {
214 Self::default()
215 }
216
217 /// Set the thumbnail to send.
218 ///
219 /// # Arguments
220 ///
221 /// * `thumbnail` - The thumbnail of the media. If the `content_type` does
222 /// not support it (e.g. audio clips), it is ignored.
223 #[must_use]
224 pub fn thumbnail(mut self, thumbnail: Option<Thumbnail>) -> Self {
225 self.thumbnail = thumbnail;
226 self
227 }
228
229 /// Set the transaction ID to send.
230 ///
231 /// # Arguments
232 ///
233 /// * `txn_id` - A unique ID that can be attached to a `MessageEvent` held
234 /// in its unsigned field as `transaction_id`. If not given, one is
235 /// created for the message.
236 #[must_use]
237 pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
238 self.txn_id = Some(txn_id);
239 self
240 }
241
242 /// Set the media metadata to send.
243 ///
244 /// # Arguments
245 ///
246 /// * `info` - The metadata of the media. If the `AttachmentInfo` type
247 /// doesn't match the `content_type`, it is ignored.
248 #[must_use]
249 pub fn info(mut self, info: AttachmentInfo) -> Self {
250 self.info = Some(info);
251 self
252 }
253
254 /// Set the optional caption.
255 ///
256 /// # Arguments
257 ///
258 /// * `caption` - The optional caption.
259 pub fn caption(mut self, caption: Option<String>) -> Self {
260 self.caption = caption;
261 self
262 }
263
264 /// Set the optional formatted caption.
265 ///
266 /// # Arguments
267 ///
268 /// * `formatted_caption` - The optional formatted caption.
269 pub fn formatted_caption(mut self, formatted_caption: Option<FormattedBody>) -> Self {
270 self.formatted_caption = formatted_caption;
271 self
272 }
273
274 /// Set the mentions of the message.
275 ///
276 /// # Arguments
277 ///
278 /// * `mentions` - The mentions of the message.
279 pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
280 self.mentions = mentions;
281 self
282 }
283
284 /// Set the reply information of the message.
285 ///
286 /// # Arguments
287 ///
288 /// * `reply` - The reply information of the message.
289 pub fn reply(mut self, reply: Option<Reply>) -> Self {
290 self.reply = reply;
291 self
292 }
293}
294
295/// Configuration for sending a gallery.
296#[cfg(feature = "unstable-msc4274")]
297#[derive(Debug, Default)]
298pub struct GalleryConfig {
299 pub(crate) txn_id: Option<OwnedTransactionId>,
300 pub(crate) items: Vec<GalleryItemInfo>,
301 pub(crate) caption: Option<String>,
302 pub(crate) formatted_caption: Option<FormattedBody>,
303 pub(crate) mentions: Option<Mentions>,
304 pub(crate) reply: Option<Reply>,
305}
306
307#[cfg(feature = "unstable-msc4274")]
308impl GalleryConfig {
309 /// Create a new empty `GalleryConfig`.
310 pub fn new() -> Self {
311 Self::default()
312 }
313
314 /// Set the transaction ID to send.
315 ///
316 /// # Arguments
317 ///
318 /// * `txn_id` - A unique ID that can be attached to a `MessageEvent` held
319 /// in its unsigned field as `transaction_id`. If not given, one is
320 /// created for the message.
321 #[must_use]
322 pub fn txn_id(mut self, txn_id: OwnedTransactionId) -> Self {
323 self.txn_id = Some(txn_id);
324 self
325 }
326
327 /// Adds a media item to the gallery.
328 ///
329 /// # Arguments
330 ///
331 /// * `item` - Information about the item to be added.
332 #[must_use]
333 pub fn add_item(mut self, item: GalleryItemInfo) -> Self {
334 self.items.push(item);
335 self
336 }
337
338 /// Set the optional caption.
339 ///
340 /// # Arguments
341 ///
342 /// * `caption` - The optional caption.
343 pub fn caption(mut self, caption: Option<String>) -> Self {
344 self.caption = caption;
345 self
346 }
347
348 /// Set the optional formatted caption.
349 ///
350 /// # Arguments
351 ///
352 /// * `formatted_caption` - The optional formatted caption.
353 pub fn formatted_caption(mut self, formatted_caption: Option<FormattedBody>) -> Self {
354 self.formatted_caption = formatted_caption;
355 self
356 }
357
358 /// Set the mentions of the message.
359 ///
360 /// # Arguments
361 ///
362 /// * `mentions` - The mentions of the message.
363 pub fn mentions(mut self, mentions: Option<Mentions>) -> Self {
364 self.mentions = mentions;
365 self
366 }
367
368 /// Set the reply information of the message.
369 ///
370 /// # Arguments
371 ///
372 /// * `reply` - The reply information of the message.
373 pub fn reply(mut self, reply: Option<Reply>) -> Self {
374 self.reply = reply;
375 self
376 }
377
378 /// Returns the number of media items in the gallery.
379 pub fn len(&self) -> usize {
380 self.items.len()
381 }
382
383 /// Checks whether the gallery contains any media items or not.
384 pub fn is_empty(&self) -> bool {
385 self.items.is_empty()
386 }
387}
388
389#[cfg(feature = "unstable-msc4274")]
390#[derive(Debug)]
391/// Metadata for a gallery item
392pub struct GalleryItemInfo {
393 /// The filename.
394 pub filename: String,
395 /// The mime type.
396 pub content_type: mime::Mime,
397 /// The binary data.
398 pub data: Vec<u8>,
399 /// The attachment info.
400 pub attachment_info: AttachmentInfo,
401 /// The caption.
402 pub caption: Option<String>,
403 /// The formatted caption.
404 pub formatted_caption: Option<FormattedBody>,
405 /// The thumbnail.
406 pub thumbnail: Option<Thumbnail>,
407}