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