ruma_events/room/message/
without_relation.rs1use serde::Serialize;
2
3use super::{
4 AddMentions, ForwardThread, MessageType, Relation, ReplacementMetadata, ReplyMetadata,
5 ReplyWithinThread, RoomMessageEventContent,
6};
7use crate::{
8 relation::{InReplyTo, Replacement, Thread},
9 Mentions,
10};
11
12#[derive(Clone, Debug, Serialize)]
14#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
15pub struct RoomMessageEventContentWithoutRelation {
16 #[serde(flatten)]
20 pub msgtype: MessageType,
21
22 #[serde(rename = "m.mentions", skip_serializing_if = "Option::is_none")]
26 pub mentions: Option<Mentions>,
27}
28
29impl RoomMessageEventContentWithoutRelation {
30 pub fn new(msgtype: MessageType) -> Self {
32 Self { msgtype, mentions: None }
33 }
34
35 pub fn text_plain(body: impl Into<String>) -> Self {
37 Self::new(MessageType::text_plain(body))
38 }
39
40 pub fn text_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
42 Self::new(MessageType::text_html(body, html_body))
43 }
44
45 #[cfg(feature = "markdown")]
47 pub fn text_markdown(body: impl AsRef<str> + Into<String>) -> Self {
48 Self::new(MessageType::text_markdown(body))
49 }
50
51 pub fn notice_plain(body: impl Into<String>) -> Self {
53 Self::new(MessageType::notice_plain(body))
54 }
55
56 pub fn notice_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
58 Self::new(MessageType::notice_html(body, html_body))
59 }
60
61 #[cfg(feature = "markdown")]
63 pub fn notice_markdown(body: impl AsRef<str> + Into<String>) -> Self {
64 Self::new(MessageType::notice_markdown(body))
65 }
66
67 pub fn emote_plain(body: impl Into<String>) -> Self {
69 Self::new(MessageType::emote_plain(body))
70 }
71
72 pub fn emote_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
74 Self::new(MessageType::emote_html(body, html_body))
75 }
76
77 #[cfg(feature = "markdown")]
79 pub fn emote_markdown(body: impl AsRef<str> + Into<String>) -> Self {
80 Self::new(MessageType::emote_markdown(body))
81 }
82
83 pub fn with_relation(
85 self,
86 relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>>,
87 ) -> RoomMessageEventContent {
88 let Self { msgtype, mentions } = self;
89 RoomMessageEventContent { msgtype, relates_to, mentions }
90 }
91
92 #[track_caller]
101 pub fn make_reply_to<'a>(
102 mut self,
103 metadata: impl Into<ReplyMetadata<'a>>,
104 forward_thread: ForwardThread,
105 add_mentions: AddMentions,
106 ) -> RoomMessageEventContent {
107 let metadata = metadata.into();
108 let original_event_id = metadata.event_id.to_owned();
109
110 let original_thread_id = metadata
111 .thread
112 .filter(|_| forward_thread == ForwardThread::Yes)
113 .map(|thread| thread.event_id.clone());
114 let relates_to = if let Some(event_id) = original_thread_id {
115 Relation::Thread(Thread::plain(event_id.to_owned(), original_event_id.to_owned()))
116 } else {
117 Relation::Reply { in_reply_to: InReplyTo { event_id: original_event_id.to_owned() } }
118 };
119
120 if add_mentions == AddMentions::Yes {
121 self.mentions
122 .get_or_insert_with(Mentions::new)
123 .user_ids
124 .insert(metadata.sender.to_owned());
125 }
126
127 self.with_relation(Some(relates_to))
128 }
129
130 pub fn make_for_thread<'a>(
145 self,
146 metadata: impl Into<ReplyMetadata<'a>>,
147 is_reply: ReplyWithinThread,
148 add_mentions: AddMentions,
149 ) -> RoomMessageEventContent {
150 let metadata = metadata.into();
151
152 let mut content = if is_reply == ReplyWithinThread::Yes {
153 self.make_reply_to(metadata, ForwardThread::No, add_mentions)
154 } else {
155 self.into()
156 };
157
158 let thread_root = if let Some(Thread { event_id, .. }) = &metadata.thread {
159 event_id.to_owned()
160 } else {
161 metadata.event_id.to_owned()
162 };
163
164 content.relates_to = Some(Relation::Thread(Thread {
165 event_id: thread_root,
166 in_reply_to: Some(InReplyTo { event_id: metadata.event_id.to_owned() }),
167 is_falling_back: is_reply == ReplyWithinThread::No,
168 }));
169
170 content
171 }
172
173 #[track_caller]
192 pub fn make_replacement(
193 mut self,
194 metadata: impl Into<ReplacementMetadata>,
195 ) -> RoomMessageEventContent {
196 let metadata = metadata.into();
197
198 let mentions = self.mentions.take();
199
200 if let Some(mentions) = &mentions {
202 let new_mentions = metadata
203 .mentions
204 .map(|old_mentions| {
205 let mut new_mentions = Mentions::new();
206
207 new_mentions.user_ids = mentions
208 .user_ids
209 .iter()
210 .filter(|u| !old_mentions.user_ids.contains(*u))
211 .cloned()
212 .collect();
213
214 new_mentions.room = mentions.room && !old_mentions.room;
215
216 new_mentions
217 })
218 .unwrap_or_else(|| mentions.clone());
219
220 self.mentions = Some(new_mentions);
221 }
222
223 let relates_to = Relation::Replacement(Replacement {
225 event_id: metadata.event_id,
226 new_content: RoomMessageEventContentWithoutRelation {
227 msgtype: self.msgtype.clone(),
228 mentions,
229 },
230 });
231
232 self.msgtype.make_replacement_body();
233
234 let mut content = RoomMessageEventContent::from(self);
235 content.relates_to = Some(relates_to);
236
237 content
238 }
239
240 pub fn add_mentions(mut self, mentions: Mentions) -> Self {
248 self.mentions.get_or_insert_with(Mentions::new).add(mentions);
249 self
250 }
251}
252
253impl From<MessageType> for RoomMessageEventContentWithoutRelation {
254 fn from(msgtype: MessageType) -> Self {
255 Self::new(msgtype)
256 }
257}
258
259impl From<RoomMessageEventContent> for RoomMessageEventContentWithoutRelation {
260 fn from(value: RoomMessageEventContent) -> Self {
261 let RoomMessageEventContent { msgtype, mentions, .. } = value;
262 Self { msgtype, mentions }
263 }
264}
265
266impl From<RoomMessageEventContentWithoutRelation> for RoomMessageEventContent {
267 fn from(value: RoomMessageEventContentWithoutRelation) -> Self {
268 let RoomMessageEventContentWithoutRelation { msgtype, mentions } = value;
269 Self { msgtype, relates_to: None, mentions }
270 }
271}