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