Skip to main content

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