ruma_events/room/
topic.rs1use ruma_macros::EventContent;
6use serde::{Deserialize, Serialize};
7
8use crate::{message::TextContentBlock, EmptyStateKey};
9
10#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
14#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
15#[ruma_event(type = "m.room.topic", kind = State, state_key_type = EmptyStateKey)]
16pub struct RoomTopicEventContent {
17 pub topic: String,
22
23 #[serde(rename = "m.topic", default, skip_serializing_if = "TopicContentBlock::is_empty")]
28 #[cfg_attr(
29 feature = "compat-lax-room-topic-deser",
30 serde(deserialize_with = "ruma_common::serde::default_on_error")
31 )]
32 pub topic_block: TopicContentBlock,
33}
34
35impl RoomTopicEventContent {
36 pub fn new(topic: String) -> Self {
38 Self { topic_block: TopicContentBlock::plain(topic.clone()), topic }
39 }
40
41 pub fn html(plain: impl Into<String>, html: impl Into<String>) -> Self {
43 let plain = plain.into();
44 Self { topic: plain.clone(), topic_block: TopicContentBlock::html(plain, html) }
45 }
46
47 #[cfg(feature = "markdown")]
52 pub fn markdown(topic: impl AsRef<str> + Into<String>) -> Self {
53 let plain = topic.as_ref().to_owned();
54 Self { topic: plain, topic_block: TopicContentBlock::markdown(topic) }
55 }
56}
57
58#[derive(Clone, Debug, Default, Serialize, Deserialize)]
63#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
64pub struct TopicContentBlock {
65 #[serde(rename = "m.text")]
67 pub text: TextContentBlock,
68}
69
70impl TopicContentBlock {
71 pub fn plain(body: impl Into<String>) -> Self {
73 Self { text: TextContentBlock::plain(body) }
74 }
75
76 pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
78 Self { text: TextContentBlock::html(body, html_body) }
79 }
80
81 #[cfg(feature = "markdown")]
86 pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
87 Self { text: TextContentBlock::markdown(body) }
88 }
89
90 fn is_empty(&self) -> bool {
92 self.text.is_empty()
93 }
94}
95
96impl From<TextContentBlock> for TopicContentBlock {
97 fn from(text: TextContentBlock) -> Self {
98 Self { text }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
105
106 use super::RoomTopicEventContent;
107 use crate::message::TextContentBlock;
108
109 #[test]
110 fn serialize_content() {
111 let mut content = RoomTopicEventContent::new("Hot Topic".to_owned());
113 assert_eq!(
114 to_json_value(&content).unwrap(),
115 json!({
116 "topic": "Hot Topic",
117 "m.topic": {
118 "m.text": [
119 { "body": "Hot Topic" },
120 ],
121 }
122 })
123 );
124
125 content.topic_block.text = TextContentBlock::from(vec![]);
127 assert_eq!(
128 to_json_value(&content).unwrap(),
129 json!({
130 "topic": "Hot Topic",
131 })
132 );
133
134 let content = RoomTopicEventContent::html("Hot Topic", "<strong>Hot</strong> Topic");
136 assert_eq!(
137 to_json_value(&content).unwrap(),
138 json!({
139 "topic": "Hot Topic",
140 "m.topic": {
141 "m.text": [
142 { "body": "<strong>Hot</strong> Topic", "mimetype": "text/html" },
143 { "body": "Hot Topic" },
144 ],
145 }
146 })
147 );
148 }
149
150 #[test]
151 fn deserialize_content() {
152 let json = json!({
153 "topic": "Hot Topic",
154 "m.topic": {
155 "m.text": [
156 { "body": "<strong>Hot</strong> Topic", "mimetype": "text/html" },
157 { "body": "Hot Topic" },
158 ],
159 }
160 });
161
162 let content = from_json_value::<RoomTopicEventContent>(json).unwrap();
163 assert_eq!(content.topic, "Hot Topic");
164 assert_eq!(content.topic_block.text.find_html(), Some("<strong>Hot</strong> Topic"));
165 assert_eq!(content.topic_block.text.find_plain(), Some("Hot Topic"));
166 }
167
168 #[test]
169 #[cfg(feature = "compat-lax-room-topic-deser")]
170 fn deserialize_invalid_content() {
171 let json = json!({
172 "topic": "Hot Topic",
173 "m.topic": [
174 { "body": "<strong>Hot</strong> Topic", "mimetype": "text/html" },
175 { "body": "Hot Topic" },
176 ],
177 });
178
179 let content = from_json_value::<RoomTopicEventContent>(json).unwrap();
180 assert_eq!(content.topic, "Hot Topic");
181 assert_eq!(content.topic_block.text.find_html(), None);
182 assert_eq!(content.topic_block.text.find_plain(), None);
183 }
184}