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}