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}