1use std::fmt::Debug;
6
7use js_int::UInt;
8use ruma_common::{
9 serde::{JsonObject, Raw, StringEnum},
10 OwnedEventId,
11};
12use serde::{Deserialize, Serialize};
13
14use super::AnyMessageLikeEvent;
15use crate::PrivOwnedStr;
16
17mod rel_serde;
18
19#[derive(Clone, Debug, Deserialize, Serialize)]
23#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
24pub struct InReplyTo {
25 pub event_id: OwnedEventId,
27}
28
29impl InReplyTo {
30 pub fn new(event_id: OwnedEventId) -> Self {
32 Self { event_id }
33 }
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize)]
40#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
41#[serde(tag = "rel_type", rename = "m.annotation")]
42pub struct Annotation {
43 pub event_id: OwnedEventId,
45
46 pub key: String,
53}
54
55impl Annotation {
56 pub fn new(event_id: OwnedEventId, key: String) -> Self {
58 Self { event_id, key }
59 }
60}
61
62#[derive(Clone, Debug)]
66#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
67pub struct Replacement<C> {
68 pub event_id: OwnedEventId,
70
71 pub new_content: C,
73}
74
75impl<C> Replacement<C> {
76 pub fn new(event_id: OwnedEventId, new_content: C) -> Self {
78 Self { event_id, new_content }
79 }
80}
81
82#[derive(Clone, Debug, Serialize, Deserialize)]
86#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
87#[serde(tag = "rel_type", rename = "m.thread")]
88pub struct Thread {
89 pub event_id: OwnedEventId,
91
92 #[serde(rename = "m.in_reply_to", skip_serializing_if = "Option::is_none")]
101 pub in_reply_to: Option<InReplyTo>,
102
103 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
106 pub is_falling_back: bool,
107}
108
109impl Thread {
110 pub fn plain(event_id: OwnedEventId, latest_event_id: OwnedEventId) -> Self {
113 Self { event_id, in_reply_to: Some(InReplyTo::new(latest_event_id)), is_falling_back: true }
114 }
115
116 pub fn without_fallback(event_id: OwnedEventId) -> Self {
119 Self { event_id, in_reply_to: None, is_falling_back: false }
120 }
121
122 pub fn reply(event_id: OwnedEventId, reply_to_event_id: OwnedEventId) -> Self {
125 Self {
126 event_id,
127 in_reply_to: Some(InReplyTo::new(reply_to_event_id)),
128 is_falling_back: false,
129 }
130 }
131}
132
133#[derive(Clone, Debug, Deserialize, Serialize)]
135#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
136pub struct BundledThread {
137 pub latest_event: Raw<AnyMessageLikeEvent>,
139
140 pub count: UInt,
142
143 pub current_user_participated: bool,
145}
146
147impl BundledThread {
148 pub fn new(
150 latest_event: Raw<AnyMessageLikeEvent>,
151 count: UInt,
152 current_user_participated: bool,
153 ) -> Self {
154 Self { latest_event, count, current_user_participated }
155 }
156}
157
158#[derive(Clone, Debug, Deserialize, Serialize)]
162#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
163#[serde(tag = "rel_type", rename = "m.reference")]
164pub struct Reference {
165 pub event_id: OwnedEventId,
167}
168
169impl Reference {
170 pub fn new(event_id: OwnedEventId) -> Self {
172 Self { event_id }
173 }
174}
175
176#[derive(Clone, Debug, Deserialize, Serialize)]
178#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
179pub struct BundledReference {
180 pub event_id: OwnedEventId,
182}
183
184impl BundledReference {
185 pub fn new(event_id: OwnedEventId) -> Self {
187 Self { event_id }
188 }
189}
190
191#[derive(Clone, Debug, Default, Deserialize, Serialize)]
193#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
194pub struct ReferenceChunk {
195 pub chunk: Vec<BundledReference>,
197}
198
199impl ReferenceChunk {
200 pub fn new(chunk: Vec<BundledReference>) -> Self {
202 Self { chunk }
203 }
204}
205
206#[derive(Clone, Debug, Serialize)]
210#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
211pub struct BundledMessageLikeRelations<E> {
212 #[serde(rename = "m.replace", skip_serializing_if = "Option::is_none")]
214 pub replace: Option<Box<E>>,
215
216 #[serde(skip_serializing)]
220 has_invalid_replacement: bool,
221
222 #[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
224 pub thread: Option<Box<BundledThread>>,
225
226 #[serde(rename = "m.reference", skip_serializing_if = "Option::is_none")]
228 pub reference: Option<Box<ReferenceChunk>>,
229}
230
231impl<E> BundledMessageLikeRelations<E> {
232 pub const fn new() -> Self {
234 Self { replace: None, has_invalid_replacement: false, thread: None, reference: None }
235 }
236
237 pub fn has_replacement(&self) -> bool {
244 self.replace.is_some() || self.has_invalid_replacement
245 }
246
247 pub fn is_empty(&self) -> bool {
249 self.replace.is_none() && self.thread.is_none() && self.reference.is_none()
250 }
251
252 pub(crate) fn map_replace<T>(self, f: impl FnOnce(E) -> T) -> BundledMessageLikeRelations<T> {
255 let Self { replace, has_invalid_replacement, thread, reference } = self;
256 let replace = replace.map(|r| Box::new(f(*r)));
257 BundledMessageLikeRelations { replace, has_invalid_replacement, thread, reference }
258 }
259}
260
261impl<E> Default for BundledMessageLikeRelations<E> {
262 fn default() -> Self {
263 Self::new()
264 }
265}
266
267#[derive(Clone, Debug, Default, Deserialize, Serialize)]
271#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
272pub struct BundledStateRelations {
273 #[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
275 pub thread: Option<Box<BundledThread>>,
276
277 #[serde(rename = "m.reference", skip_serializing_if = "Option::is_none")]
279 pub reference: Option<Box<ReferenceChunk>>,
280}
281
282impl BundledStateRelations {
283 pub const fn new() -> Self {
285 Self { thread: None, reference: None }
286 }
287
288 pub fn is_empty(&self) -> bool {
290 self.thread.is_none() && self.reference.is_none()
291 }
292}
293
294#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
296#[derive(Clone, PartialEq, Eq, StringEnum)]
297#[ruma_enum(rename_all = "m.snake_case")]
298#[non_exhaustive]
299pub enum RelationType {
300 Annotation,
302
303 Replacement,
305
306 Thread,
308
309 Reference,
311
312 #[doc(hidden)]
313 _Custom(PrivOwnedStr),
314}
315
316#[doc(hidden)]
318#[derive(Clone, Debug, Deserialize, Serialize)]
319#[serde(transparent)]
320pub struct CustomRelation(pub(super) JsonObject);
321
322impl CustomRelation {
323 pub(super) fn rel_type(&self) -> Option<RelationType> {
324 Some(self.0.get("rel_type")?.as_str()?.into())
325 }
326}