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 crate::{AnySyncMessageLikeEvent, PrivOwnedStr};
15
16mod rel_serde;
17
18#[derive(Clone, Debug, Deserialize, Serialize)]
22#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
23pub struct InReplyTo {
24 pub event_id: OwnedEventId,
26}
27
28impl InReplyTo {
29 pub fn new(event_id: OwnedEventId) -> Self {
31 Self { event_id }
32 }
33}
34
35#[derive(Clone, Debug, Deserialize, Serialize)]
39#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
40#[serde(tag = "rel_type", rename = "m.annotation")]
41pub struct Annotation {
42 pub event_id: OwnedEventId,
44
45 pub key: String,
52}
53
54impl Annotation {
55 pub fn new(event_id: OwnedEventId, key: String) -> Self {
57 Self { event_id, key }
58 }
59}
60
61#[derive(Clone, Debug)]
65#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
66pub struct Replacement<C> {
67 pub event_id: OwnedEventId,
69
70 pub new_content: C,
72}
73
74impl<C> Replacement<C> {
75 pub fn new(event_id: OwnedEventId, new_content: C) -> Self {
77 Self { event_id, new_content }
78 }
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
85#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
86#[serde(tag = "rel_type", rename = "m.thread")]
87pub struct Thread {
88 pub event_id: OwnedEventId,
90
91 #[serde(rename = "m.in_reply_to", skip_serializing_if = "Option::is_none")]
100 pub in_reply_to: Option<InReplyTo>,
101
102 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
105 pub is_falling_back: bool,
106}
107
108impl Thread {
109 pub fn plain(event_id: OwnedEventId, latest_event_id: OwnedEventId) -> Self {
112 Self { event_id, in_reply_to: Some(InReplyTo::new(latest_event_id)), is_falling_back: true }
113 }
114
115 pub fn without_fallback(event_id: OwnedEventId) -> Self {
118 Self { event_id, in_reply_to: None, is_falling_back: false }
119 }
120
121 pub fn reply(event_id: OwnedEventId, reply_to_event_id: OwnedEventId) -> Self {
124 Self {
125 event_id,
126 in_reply_to: Some(InReplyTo::new(reply_to_event_id)),
127 is_falling_back: false,
128 }
129 }
130}
131
132#[derive(Clone, Debug, Deserialize, Serialize)]
134#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
135pub struct BundledThread {
136 pub latest_event: Raw<AnySyncMessageLikeEvent>,
138
139 pub count: UInt,
141
142 pub current_user_participated: bool,
144}
145
146impl BundledThread {
147 pub fn new(
149 latest_event: Raw<AnySyncMessageLikeEvent>,
150 count: UInt,
151 current_user_participated: bool,
152 ) -> Self {
153 Self { latest_event, count, current_user_participated }
154 }
155}
156
157#[derive(Clone, Debug, Deserialize, Serialize)]
161#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
162#[serde(tag = "rel_type", rename = "m.reference")]
163pub struct Reference {
164 pub event_id: OwnedEventId,
166}
167
168impl Reference {
169 pub fn new(event_id: OwnedEventId) -> Self {
171 Self { event_id }
172 }
173}
174
175#[derive(Clone, Debug, Deserialize, Serialize)]
177#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
178pub struct BundledReference {
179 pub event_id: OwnedEventId,
181}
182
183impl BundledReference {
184 pub fn new(event_id: OwnedEventId) -> Self {
186 Self { event_id }
187 }
188}
189
190#[derive(Clone, Debug, Default, Deserialize, Serialize)]
192#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
193pub struct ReferenceChunk {
194 pub chunk: Vec<BundledReference>,
196}
197
198impl ReferenceChunk {
199 pub fn new(chunk: Vec<BundledReference>) -> Self {
201 Self { chunk }
202 }
203}
204
205#[derive(Clone, Debug, Serialize)]
209#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
210pub struct BundledMessageLikeRelations<E> {
211 #[serde(rename = "m.replace", skip_serializing_if = "Option::is_none")]
213 pub replace: Option<Box<E>>,
214
215 #[serde(skip_serializing)]
219 has_invalid_replacement: bool,
220
221 #[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
223 pub thread: Option<Box<BundledThread>>,
224
225 #[serde(rename = "m.reference", skip_serializing_if = "Option::is_none")]
227 pub reference: Option<Box<ReferenceChunk>>,
228}
229
230impl<E> BundledMessageLikeRelations<E> {
231 pub const fn new() -> Self {
233 Self { replace: None, has_invalid_replacement: false, thread: None, reference: None }
234 }
235
236 pub fn has_replacement(&self) -> bool {
243 self.replace.is_some() || self.has_invalid_replacement
244 }
245
246 pub fn is_empty(&self) -> bool {
248 self.replace.is_none() && self.thread.is_none() && self.reference.is_none()
249 }
250
251 pub(crate) fn map_replace<T>(self, f: impl FnOnce(E) -> T) -> BundledMessageLikeRelations<T> {
254 let Self { replace, has_invalid_replacement, thread, reference } = self;
255 let replace = replace.map(|r| Box::new(f(*r)));
256 BundledMessageLikeRelations { replace, has_invalid_replacement, thread, reference }
257 }
258}
259
260impl<E> Default for BundledMessageLikeRelations<E> {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266#[derive(Clone, Debug, Default, Deserialize, Serialize)]
270#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
271pub struct BundledStateRelations {
272 #[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
274 pub thread: Option<Box<BundledThread>>,
275
276 #[serde(rename = "m.reference", skip_serializing_if = "Option::is_none")]
278 pub reference: Option<Box<ReferenceChunk>>,
279}
280
281impl BundledStateRelations {
282 pub const fn new() -> Self {
284 Self { thread: None, reference: None }
285 }
286
287 pub fn is_empty(&self) -> bool {
289 self.thread.is_none() && self.reference.is_none()
290 }
291}
292
293#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
295#[derive(Clone, PartialEq, Eq, StringEnum)]
296#[ruma_enum(rename_all = "m.snake_case")]
297#[non_exhaustive]
298pub enum RelationType {
299 Annotation,
301
302 #[ruma_enum(rename = "m.replace")]
304 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}