Skip to main content

ruma_events/room/message/
content_serde.rs

1//! `Deserialize` implementation for RoomMessageEventContent and MessageType.
2
3use as_variant::as_variant;
4use ruma_common::serde::{JsonObject, from_raw_json_value};
5use serde::{Deserialize, de};
6use serde_json::{Value as JsonValue, value::RawValue as RawJsonValue};
7
8#[cfg(feature = "unstable-msc4274")]
9use super::gallery::{CustomGalleryItemContent, GalleryItemType};
10use super::{
11    MessageType, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
12    relation_serde::deserialize_relation,
13};
14#[cfg(feature = "unstable-msc4471")]
15use crate::stream::StreamDescriptor;
16use crate::{Mentions, room::message::CustomMessageContent};
17
18impl<'de> Deserialize<'de> for RoomMessageEventContent {
19    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
20    where
21        D: de::Deserializer<'de>,
22    {
23        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
24
25        let mut deserializer = serde_json::Deserializer::from_str(json.get());
26        let relates_to = deserialize_relation(&mut deserializer).map_err(de::Error::custom)?;
27
28        let MentionsDeHelper {
29            mentions,
30            #[cfg(feature = "unstable-msc4471")]
31            stream,
32        } = from_raw_json_value(&json)?;
33
34        Ok(Self {
35            msgtype: from_raw_json_value(&json)?,
36            relates_to,
37            mentions,
38            #[cfg(feature = "unstable-msc4471")]
39            stream,
40        })
41    }
42}
43
44impl<'de> Deserialize<'de> for RoomMessageEventContentWithoutRelation {
45    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46    where
47        D: de::Deserializer<'de>,
48    {
49        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
50
51        let MentionsDeHelper {
52            mentions,
53            #[cfg(feature = "unstable-msc4471")]
54            stream,
55        } = from_raw_json_value(&json)?;
56
57        Ok(Self {
58            msgtype: from_raw_json_value(&json)?,
59            mentions,
60            #[cfg(feature = "unstable-msc4471")]
61            stream,
62        })
63    }
64}
65
66#[derive(Deserialize)]
67struct MentionsDeHelper {
68    #[serde(rename = "m.mentions")]
69    mentions: Option<Mentions>,
70
71    #[cfg(feature = "unstable-msc4471")]
72    #[serde(rename = "org.matrix.msc4471.stream")]
73    stream: Option<StreamDescriptor>,
74}
75
76/// Helper struct to determine the msgtype from a `serde_json::value::RawValue`
77#[derive(Debug, Deserialize)]
78struct MessageTypeDeHelper {
79    /// The message type field
80    msgtype: String,
81}
82
83impl<'de> Deserialize<'de> for MessageType {
84    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85    where
86        D: de::Deserializer<'de>,
87    {
88        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
89        let MessageTypeDeHelper { msgtype } = from_raw_json_value(&json)?;
90
91        Ok(match msgtype.as_ref() {
92            "m.audio" => Self::Audio(from_raw_json_value(&json)?),
93            "m.emote" => Self::Emote(from_raw_json_value(&json)?),
94            "m.file" => Self::File(from_raw_json_value(&json)?),
95            #[cfg(feature = "unstable-msc4274")]
96            "dm.filament.gallery" => Self::Gallery(from_raw_json_value(&json)?),
97            "m.image" => Self::Image(from_raw_json_value(&json)?),
98            "m.location" => Self::Location(from_raw_json_value(&json)?),
99            "m.notice" => Self::Notice(from_raw_json_value(&json)?),
100            "m.server_notice" => Self::ServerNotice(from_raw_json_value(&json)?),
101            "m.text" => Self::Text(from_raw_json_value(&json)?),
102            "m.video" => Self::Video(from_raw_json_value(&json)?),
103            "m.key.verification.request" => Self::VerificationRequest(from_raw_json_value(&json)?),
104            _ => {
105                let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
106                data.remove("msgtype");
107                let body = data
108                    .remove("body")
109                    .and_then(|value| as_variant!(value, JsonValue::String))
110                    .ok_or_else(|| de::Error::missing_field("body"))?;
111
112                Self::_Custom(CustomMessageContent { msgtype, body, data })
113            }
114        })
115    }
116}
117
118/// Helper struct to determine the itemtype from a `serde_json::value::RawValue`
119#[derive(Debug, Deserialize)]
120#[cfg(feature = "unstable-msc4274")]
121struct ItemTypeDeHelper {
122    /// The item type field
123    itemtype: String,
124}
125
126#[cfg(feature = "unstable-msc4274")]
127impl<'de> Deserialize<'de> for GalleryItemType {
128    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
129    where
130        D: de::Deserializer<'de>,
131    {
132        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
133        let ItemTypeDeHelper { itemtype } = from_raw_json_value(&json)?;
134
135        Ok(match itemtype.as_ref() {
136            "m.audio" => Self::Audio(from_raw_json_value(&json)?),
137            "m.file" => Self::File(from_raw_json_value(&json)?),
138            "m.image" => Self::Image(from_raw_json_value(&json)?),
139            "m.video" => Self::Video(from_raw_json_value(&json)?),
140            _ => {
141                let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
142                data.remove("itemtype");
143                let body = data
144                    .remove("body")
145                    .and_then(|value| as_variant!(value, JsonValue::String))
146                    .ok_or_else(|| de::Error::missing_field("body"))?;
147
148                Self::_Custom(CustomGalleryItemContent { itemtype, body, data })
149            }
150        })
151    }
152}
153
154#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/112615
155#[cfg(feature = "unstable-msc3488")]
156pub(in super::super) mod msc3488 {
157    use ruma_common::MilliSecondsSinceUnixEpoch;
158    use serde::{Deserialize, Serialize};
159
160    use crate::{
161        location::{AssetContent, LocationContent},
162        message::historical_serde::MessageContentBlock,
163        room::message::{LocationInfo, LocationMessageEventContent},
164    };
165
166    /// Deserialize helper type for `LocationMessageEventContent` with unstable fields from msc3488.
167    #[derive(Serialize, Deserialize)]
168    pub(in super::super) struct LocationMessageEventContentSerDeHelper {
169        pub body: String,
170
171        pub geo_uri: String,
172
173        #[serde(skip_serializing_if = "Option::is_none")]
174        pub info: Option<Box<LocationInfo>>,
175
176        #[serde(flatten)]
177        pub message: Option<MessageContentBlock>,
178
179        #[serde(rename = "org.matrix.msc3488.location", skip_serializing_if = "Option::is_none")]
180        pub location: Option<LocationContent>,
181
182        #[serde(rename = "org.matrix.msc3488.asset", skip_serializing_if = "Option::is_none")]
183        pub asset: Option<AssetContent>,
184
185        #[serde(rename = "org.matrix.msc3488.ts", skip_serializing_if = "Option::is_none")]
186        pub ts: Option<MilliSecondsSinceUnixEpoch>,
187    }
188
189    impl From<LocationMessageEventContent> for LocationMessageEventContentSerDeHelper {
190        fn from(value: LocationMessageEventContent) -> Self {
191            let LocationMessageEventContent { body, geo_uri, info, message, location, asset, ts } =
192                value;
193
194            Self { body, geo_uri, info, message: message.map(Into::into), location, asset, ts }
195        }
196    }
197
198    impl From<LocationMessageEventContentSerDeHelper> for LocationMessageEventContent {
199        fn from(value: LocationMessageEventContentSerDeHelper) -> Self {
200            let LocationMessageEventContentSerDeHelper {
201                body,
202                geo_uri,
203                info,
204                message,
205                location,
206                asset,
207                ts,
208            } = value;
209
210            LocationMessageEventContent {
211                body,
212                geo_uri,
213                info,
214                message: message.map(Into::into),
215                location,
216                asset,
217                ts,
218            }
219        }
220    }
221}