ruma_events/message/
historical_serde.rs

1//! Serde for old versions of MSC1767 still used in some types ([spec]).
2//!
3//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/blob/d6046d8402e7a3c7a4fcbc9da16ea9bad5968992/proposals/1767-extensible-events.md
4
5use serde::{Deserialize, Serialize};
6
7use super::{TextContentBlock, TextRepresentation};
8
9/// Historical `m.message` text content block from MSC1767.
10#[derive(Clone, Default, Serialize, Deserialize)]
11#[serde(try_from = "MessageContentBlockSerDeHelper")]
12#[serde(into = "MessageContentBlockSerDeHelper")]
13pub(crate) struct MessageContentBlock(Vec<TextRepresentation>);
14
15impl From<MessageContentBlock> for TextContentBlock {
16    fn from(value: MessageContentBlock) -> Self {
17        Self(value.0)
18    }
19}
20
21impl From<TextContentBlock> for MessageContentBlock {
22    fn from(value: TextContentBlock) -> Self {
23        Self(value.0)
24    }
25}
26
27#[derive(Default, Serialize, Deserialize)]
28pub(crate) struct MessageContentBlockSerDeHelper {
29    /// Plain text short form.
30    #[serde(rename = "org.matrix.msc1767.text", skip_serializing_if = "Option::is_none")]
31    text: Option<String>,
32
33    /// HTML short form.
34    #[serde(rename = "org.matrix.msc1767.html", skip_serializing_if = "Option::is_none")]
35    html: Option<String>,
36
37    /// Long form.
38    #[serde(rename = "org.matrix.msc1767.message", skip_serializing_if = "Option::is_none")]
39    message: Option<Vec<TextRepresentation>>,
40}
41
42impl TryFrom<MessageContentBlockSerDeHelper> for Vec<TextRepresentation> {
43    type Error = &'static str;
44
45    fn try_from(value: MessageContentBlockSerDeHelper) -> Result<Self, Self::Error> {
46        let MessageContentBlockSerDeHelper { text, html, message } = value;
47
48        if let Some(message) = message {
49            Ok(message)
50        } else {
51            let message: Vec<_> = html
52                .map(TextRepresentation::html)
53                .into_iter()
54                .chain(text.map(TextRepresentation::plain))
55                .collect();
56            if !message.is_empty() {
57                Ok(message)
58            } else {
59                Err(
60                    "missing at least one of fields `org.matrix.msc1767.text`, `org.matrix.msc1767.html` or `org.matrix.msc1767.message`",
61                )
62            }
63        }
64    }
65}
66
67impl TryFrom<MessageContentBlockSerDeHelper> for MessageContentBlock {
68    type Error = &'static str;
69
70    fn try_from(value: MessageContentBlockSerDeHelper) -> Result<Self, Self::Error> {
71        Ok(Self(value.try_into()?))
72    }
73}
74
75impl From<Vec<TextRepresentation>> for MessageContentBlockSerDeHelper {
76    fn from(value: Vec<TextRepresentation>) -> Self {
77        let has_shortcut =
78            |message: &TextRepresentation| matches!(&*message.mimetype, "text/plain" | "text/html");
79
80        if value.iter().all(has_shortcut) {
81            let mut helper = Self::default();
82
83            for message in value.into_iter() {
84                if message.mimetype == "text/plain" {
85                    helper.text = Some(message.body);
86                } else if message.mimetype == "text/html" {
87                    helper.html = Some(message.body);
88                }
89            }
90
91            helper
92        } else {
93            Self { message: Some(value), ..Default::default() }
94        }
95    }
96}
97
98impl From<MessageContentBlock> for MessageContentBlockSerDeHelper {
99    fn from(value: MessageContentBlock) -> Self {
100        value.0.into()
101    }
102}