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("missing at least one of fields `org.matrix.msc1767.text`, `org.matrix.msc1767.html` or `org.matrix.msc1767.message`")
60            }
61        }
62    }
63}
64
65impl TryFrom<MessageContentBlockSerDeHelper> for MessageContentBlock {
66    type Error = &'static str;
67
68    fn try_from(value: MessageContentBlockSerDeHelper) -> Result<Self, Self::Error> {
69        Ok(Self(value.try_into()?))
70    }
71}
72
73impl From<Vec<TextRepresentation>> for MessageContentBlockSerDeHelper {
74    fn from(value: Vec<TextRepresentation>) -> Self {
75        let has_shortcut =
76            |message: &TextRepresentation| matches!(&*message.mimetype, "text/plain" | "text/html");
77
78        if value.iter().all(has_shortcut) {
79            let mut helper = Self::default();
80
81            for message in value.into_iter() {
82                if message.mimetype == "text/plain" {
83                    helper.text = Some(message.body);
84                } else if message.mimetype == "text/html" {
85                    helper.html = Some(message.body);
86                }
87            }
88
89            helper
90        } else {
91            Self { message: Some(value), ..Default::default() }
92        }
93    }
94}
95
96impl From<MessageContentBlock> for MessageContentBlockSerDeHelper {
97    fn from(value: MessageContentBlock) -> Self {
98        value.0.into()
99    }
100}