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}