Skip to main content

ruma_events/room/message/
gallery.rs

1use std::borrow::Cow;
2
3use ruma_common::serde::JsonObject;
4use serde::{Deserialize, Serialize, de::DeserializeOwned};
5use serde_json::Value as JsonValue;
6
7use super::{
8    AudioMessageEventContent, FileMessageEventContent, FormattedBody, ImageMessageEventContent,
9    VideoMessageEventContent,
10};
11
12/// The payload for a gallery message.
13#[derive(Clone, Debug, Deserialize, Serialize)]
14#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
15pub struct GalleryMessageEventContent {
16    /// A human-readable description of the gallery.
17    pub body: String,
18
19    /// Formatted form of the message `body`.
20    #[serde(flatten)]
21    pub formatted: Option<FormattedBody>,
22
23    /// Item types for the media in the gallery.
24    pub itemtypes: Vec<GalleryItemType>,
25}
26
27impl GalleryMessageEventContent {
28    /// Creates a new `GalleryMessageEventContent`.
29    pub fn new(
30        body: String,
31        formatted: Option<FormattedBody>,
32        itemtypes: Vec<GalleryItemType>,
33    ) -> Self {
34        Self { body, formatted, itemtypes }
35    }
36}
37
38/// The content that is specific to each gallery item type variant.
39#[derive(Clone, Debug, Serialize)]
40#[serde(tag = "itemtype")]
41#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
42pub enum GalleryItemType {
43    /// An audio item.
44    #[serde(rename = "m.audio")]
45    Audio(AudioMessageEventContent),
46
47    /// A file item.
48    #[serde(rename = "m.file")]
49    File(FileMessageEventContent),
50
51    /// An image item.
52    #[serde(rename = "m.image")]
53    Image(ImageMessageEventContent),
54
55    /// A video item.
56    #[serde(rename = "m.video")]
57    Video(VideoMessageEventContent),
58
59    /// A custom item.
60    #[doc(hidden)]
61    #[serde(untagged)]
62    _Custom(CustomGalleryItemContent),
63}
64
65impl GalleryItemType {
66    /// Creates a new `GalleryItemType`.
67    ///
68    /// The `itemtype` and `body` are required fields.
69    /// Additionally it's possible to add arbitrary key/value pairs to the event content for custom
70    /// item types through the `data` map.
71    ///
72    /// Prefer to use the public variants of `GalleryItemType` where possible; this constructor is
73    /// meant be used for unsupported item types only and does not allow setting arbitrary data
74    /// for supported ones.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error if the `itemtype` is known and serialization of `data` to the corresponding
79    /// `GalleryItemType` variant fails.
80    pub fn new(itemtype: &str, body: String, data: JsonObject) -> serde_json::Result<Self> {
81        fn deserialize_variant<T: DeserializeOwned>(
82            body: String,
83            mut obj: JsonObject,
84        ) -> serde_json::Result<T> {
85            obj.insert("body".into(), body.into());
86            serde_json::from_value(JsonValue::Object(obj))
87        }
88
89        Ok(match itemtype {
90            "m.audio" => Self::Audio(deserialize_variant(body, data)?),
91            "m.file" => Self::File(deserialize_variant(body, data)?),
92            "m.image" => Self::Image(deserialize_variant(body, data)?),
93            "m.video" => Self::Video(deserialize_variant(body, data)?),
94            _ => Self::_Custom(CustomGalleryItemContent {
95                itemtype: itemtype.to_owned(),
96                body,
97                data,
98            }),
99        })
100    }
101
102    /// Returns a reference to the `itemtype` string.
103    pub fn itemtype(&self) -> &str {
104        match self {
105            Self::Audio(_) => "m.audio",
106            Self::File(_) => "m.file",
107            Self::Image(_) => "m.image",
108            Self::Video(_) => "m.video",
109            Self::_Custom(c) => &c.itemtype,
110        }
111    }
112
113    /// Return a reference to the itemtype body.
114    pub fn body(&self) -> &str {
115        match self {
116            GalleryItemType::Audio(m) => &m.body,
117            GalleryItemType::File(m) => &m.body,
118            GalleryItemType::Image(m) => &m.body,
119            GalleryItemType::Video(m) => &m.body,
120            GalleryItemType::_Custom(m) => &m.body,
121        }
122    }
123
124    /// Returns the associated data.
125    ///
126    /// The returned JSON object won't contain the `itemtype` and `body` fields, use
127    /// [`.itemtype()`][Self::itemtype] / [`.body()`](Self::body) to access those.
128    ///
129    /// Prefer to use the public variants of `GalleryItemType` where possible; this method is meant
130    /// to be used for custom message types only.
131    pub fn data(&self) -> Cow<'_, JsonObject> {
132        fn serialize<T: Serialize>(obj: &T) -> JsonObject {
133            match serde_json::to_value(obj).expect("item type serialization to succeed") {
134                JsonValue::Object(mut obj) => {
135                    obj.remove("body");
136                    obj
137                }
138                _ => panic!("all item types must serialize to objects"),
139            }
140        }
141
142        match self {
143            Self::Audio(d) => Cow::Owned(serialize(d)),
144            Self::File(d) => Cow::Owned(serialize(d)),
145            Self::Image(d) => Cow::Owned(serialize(d)),
146            Self::Video(d) => Cow::Owned(serialize(d)),
147            Self::_Custom(c) => Cow::Borrowed(&c.data),
148        }
149    }
150}
151
152/// The payload for a custom gallery item type.
153#[doc(hidden)]
154#[derive(Clone, Debug, Serialize)]
155pub struct CustomGalleryItemContent {
156    /// A custom itemtype.
157    pub(super) itemtype: String,
158
159    /// The message body.
160    pub(super) body: String,
161
162    /// Remaining event content.
163    #[serde(flatten)]
164    pub(super) data: JsonObject,
165}