1use ruma::{
2 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, UserId,
3 events::{
4 AnyPossiblyRedactedStateEventContent, AnyStrippedStateEvent, AnySyncStateEvent,
5 AnySyncTimelineEvent, PossiblyRedactedStateEventContent, RedactContent,
6 RedactedStateEventContent, StateEventType, StaticEventContent, StaticStateEventContent,
7 StrippedStateEvent, SyncStateEvent,
8 room::{
9 create::{StrippedRoomCreateEvent, SyncRoomCreateEvent},
10 member::PossiblyRedactedRoomMemberEventContent,
11 },
12 },
13 room_version_rules::RedactionRules,
14 serde::Raw,
15};
16use serde::{Deserialize, Serialize, de::DeserializeOwned};
17use tracing::{error, warn};
18
19use crate::room::RoomCreateWithCreatorEventContent;
20
21#[derive(Clone, Debug, Deserialize, Serialize)]
27#[serde(
28 bound(serialize = "C: Serialize + Clone"),
29 from = "MinimalStateEventSerdeHelper<C>",
30 into = "MinimalStateEventSerdeHelper<C>"
31)]
32pub struct MinimalStateEvent<C: PossiblyRedactedStateEventContent + RedactContent> {
33 pub content: C,
35 pub event_id: Option<OwnedEventId>,
37}
38
39impl<C> MinimalStateEvent<C>
40where
41 C: PossiblyRedactedStateEventContent + RedactContent,
42 C::Redacted: Into<C>,
43{
44 pub fn redact(&mut self, rules: &RedactionRules)
48 where
49 C: Clone,
50 {
51 self.content = self.content.clone().redact(rules).into()
52 }
53}
54
55#[derive(Serialize, Deserialize)]
57enum MinimalStateEventSerdeHelper<C> {
58 Original(MinimalStateEventSerdeHelperInner<C>),
60 Redacted(MinimalStateEventSerdeHelperInner<C>),
62 PossiblyRedacted(MinimalStateEventSerdeHelperInner<C>),
64}
65
66impl<C> From<MinimalStateEventSerdeHelper<C>> for MinimalStateEvent<C>
67where
68 C: PossiblyRedactedStateEventContent + RedactContent,
69{
70 fn from(value: MinimalStateEventSerdeHelper<C>) -> Self {
71 match value {
72 MinimalStateEventSerdeHelper::Original(event) => event,
73 MinimalStateEventSerdeHelper::Redacted(event) => event,
74 MinimalStateEventSerdeHelper::PossiblyRedacted(event) => event,
75 }
76 .into()
77 }
78}
79
80impl<C> From<MinimalStateEvent<C>> for MinimalStateEventSerdeHelper<C>
81where
82 C: PossiblyRedactedStateEventContent + RedactContent,
83{
84 fn from(value: MinimalStateEvent<C>) -> Self {
85 Self::PossiblyRedacted(value.into())
86 }
87}
88
89#[derive(Serialize, Deserialize)]
90struct MinimalStateEventSerdeHelperInner<C> {
91 content: C,
92 event_id: Option<OwnedEventId>,
93}
94
95impl<C> From<MinimalStateEventSerdeHelperInner<C>> for MinimalStateEvent<C>
96where
97 C: PossiblyRedactedStateEventContent + RedactContent,
98{
99 fn from(value: MinimalStateEventSerdeHelperInner<C>) -> Self {
100 let MinimalStateEventSerdeHelperInner { content, event_id } = value;
101 Self { content, event_id }
102 }
103}
104
105impl<C> From<MinimalStateEvent<C>> for MinimalStateEventSerdeHelperInner<C>
106where
107 C: PossiblyRedactedStateEventContent + RedactContent,
108{
109 fn from(value: MinimalStateEvent<C>) -> Self {
110 let MinimalStateEvent { content, event_id } = value;
111 Self { content, event_id }
112 }
113}
114
115pub type MinimalRoomMemberEvent = MinimalStateEvent<PossiblyRedactedRoomMemberEventContent>;
117
118impl<C1, C2> From<SyncStateEvent<C1>> for MinimalStateEvent<C2>
119where
120 C1: StaticStateEventContent + RedactContent + Into<C2>,
121 C1::Redacted: RedactedStateEventContent + Into<C2>,
122 C2: PossiblyRedactedStateEventContent + RedactContent,
123{
124 fn from(ev: SyncStateEvent<C1>) -> Self {
125 match ev {
126 SyncStateEvent::Original(ev) => {
127 Self { content: ev.content.into(), event_id: Some(ev.event_id) }
128 }
129 SyncStateEvent::Redacted(ev) => {
130 Self { content: ev.content.into(), event_id: Some(ev.event_id) }
131 }
132 }
133 }
134}
135
136impl<C1, C2> From<&SyncStateEvent<C1>> for MinimalStateEvent<C2>
137where
138 C1: Clone + StaticStateEventContent + RedactContent + Into<C2>,
139 C1::Redacted: Clone + RedactedStateEventContent + Into<C2>,
140 C2: PossiblyRedactedStateEventContent + RedactContent,
141{
142 fn from(ev: &SyncStateEvent<C1>) -> Self {
143 match ev {
144 SyncStateEvent::Original(ev) => {
145 Self { content: ev.content.clone().into(), event_id: Some(ev.event_id.clone()) }
146 }
147 SyncStateEvent::Redacted(ev) => {
148 Self { content: ev.content.clone().into(), event_id: Some(ev.event_id.clone()) }
149 }
150 }
151 }
152}
153
154impl From<&SyncRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
155 fn from(ev: &SyncRoomCreateEvent) -> Self {
156 match ev {
157 SyncStateEvent::Original(ev) => Self {
158 content: RoomCreateWithCreatorEventContent::from_event_content(
159 ev.content.clone(),
160 ev.sender.clone(),
161 ),
162 event_id: Some(ev.event_id.clone()),
163 },
164 SyncStateEvent::Redacted(ev) => Self {
165 content: RoomCreateWithCreatorEventContent::from_event_content(
166 ev.content.clone(),
167 ev.sender.clone(),
168 ),
169 event_id: Some(ev.event_id.clone()),
170 },
171 }
172 }
173}
174
175impl<C> From<StrippedStateEvent<C>> for MinimalStateEvent<C>
176where
177 C: PossiblyRedactedStateEventContent + RedactContent,
178{
179 fn from(event: StrippedStateEvent<C>) -> Self {
180 Self { content: event.content, event_id: None }
181 }
182}
183
184impl<C> From<&StrippedStateEvent<C>> for MinimalStateEvent<C>
185where
186 C: Clone + PossiblyRedactedStateEventContent + RedactContent,
187{
188 fn from(event: &StrippedStateEvent<C>) -> Self {
189 Self { content: event.content.clone(), event_id: None }
190 }
191}
192
193impl From<&StrippedRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
194 fn from(event: &StrippedRoomCreateEvent) -> Self {
195 let content = RoomCreateWithCreatorEventContent::from_event_content(
196 event.content.clone(),
197 event.sender.clone(),
198 );
199 Self { content, event_id: None }
200 }
201}
202
203#[derive(Debug, Clone)]
209pub struct RawStateEventWithKeys<T: AnyStateEventEnum> {
210 pub raw: Raw<T>,
212 pub event_type: StateEventType,
214 pub state_key: String,
216 cached_event: Option<Result<T, ()>>,
218}
219
220impl<T: AnyStateEventEnum> RawStateEventWithKeys<T> {
221 pub fn try_from_raw_state_event(raw: Raw<T>) -> Option<Self> {
226 let StateEventWithKeysDeHelper { event_type, state_key } =
227 match raw.deserialize_as_unchecked() {
228 Ok(fields) => fields,
229 Err(error) => {
230 warn!(?error, "Couldn't deserialize type and state key of state event");
231 return None;
232 }
233 };
234
235 let Some(state_key) = state_key else {
237 warn!(
238 ?event_type,
239 "Couldn't deserialize type and state key of state event: missing state key"
240 );
241 return None;
242 };
243
244 Some(Self { raw, event_type, state_key, cached_event: None })
245 }
246
247 pub fn deserialize(&mut self) -> Option<&T> {
254 self.cached_event
255 .get_or_insert_with(|| {
256 self.raw.deserialize().map_err(|error| {
257 warn!(?error, "Couldn't deserialize state event");
258 })
259 })
260 .as_ref()
261 .ok()
262 }
263
264 pub fn deserialize_as_minimal_event<F, C>(
279 &mut self,
280 as_variant_fn: F,
281 ) -> Option<MinimalStateEvent<C>>
282 where
283 F: FnOnce(AnyPossiblyRedactedStateEventContent) -> Option<C>,
284 C: StaticEventContent + PossiblyRedactedStateEventContent + RedactContent,
285 {
286 let any_event = self.deserialize()?;
287 let any_content = any_event.get_content();
288
289 let Some(content) = as_variant_fn(any_content) else {
290 error!(
292 expected_event_type = ?C::TYPE,
293 actual_event_type = ?any_event.get_event_type().to_string(),
294 "Couldn't deserialize state event content: unexpected type",
295 );
296 return None;
297 };
298
299 Some(MinimalStateEvent {
300 content,
301 event_id: any_event.get_event_id().map(ToOwned::to_owned),
302 })
303 }
304
305 pub(crate) fn set_cached_event(&mut self, event: T) {
312 self.cached_event = Some(Ok(event));
313 }
314}
315
316impl RawStateEventWithKeys<AnySyncStateEvent> {
317 pub fn try_from_raw_timeline_event(raw: &Raw<AnySyncTimelineEvent>) -> Option<Self> {
323 let StateEventWithKeysDeHelper { event_type, state_key } = match raw
324 .deserialize_as_unchecked()
325 {
326 Ok(fields) => fields,
327 Err(error) => {
328 warn!(?error, "Couldn't deserialize type and optional state key of timeline event");
329 return None;
330 }
331 };
332
333 Some(Self {
335 event_type,
336 state_key: state_key?,
337 raw: raw.clone().cast_unchecked(),
338 cached_event: None,
339 })
340 }
341
342 pub fn deserialize_as<F, C>(&mut self, as_variant_fn: F) -> Option<&SyncStateEvent<C>>
356 where
357 F: FnOnce(&AnySyncStateEvent) -> Option<&SyncStateEvent<C>>,
358 C: StaticEventContent + StaticStateEventContent + RedactContent,
359 C::Redacted: RedactedStateEventContent,
360 {
361 let any_event = self.deserialize()?;
362 let event = as_variant_fn(any_event);
363
364 if event.is_none() {
365 error!(
367 expected_event_type = ?C::TYPE,
368 actual_event_type = ?any_event.event_type().to_string(),
369 "Couldn't deserialize state event: unexpected type",
370 );
371 }
372
373 event
374 }
375}
376
377impl RawStateEventWithKeys<AnyStrippedStateEvent> {
378 pub fn deserialize_as<F, C>(&mut self, as_variant_fn: F) -> Option<&StrippedStateEvent<C>>
392 where
393 F: FnOnce(&AnyStrippedStateEvent) -> Option<&StrippedStateEvent<C>>,
394 C: StaticEventContent + PossiblyRedactedStateEventContent,
395 {
396 let any_event = self.deserialize()?;
397 let event = as_variant_fn(any_event);
398
399 if event.is_none() {
400 error!(
402 expected_event_type = ?C::TYPE,
403 actual_event_type = ?any_event.event_type().to_string(),
404 "Couldn't deserialize stripped state event: unexpected type",
405 );
406 }
407
408 event
409 }
410}
411
412#[derive(Deserialize)]
414struct StateEventWithKeysDeHelper {
415 #[serde(rename = "type")]
416 event_type: StateEventType,
417 state_key: Option<String>,
420}
421
422pub trait AnyStateEventEnum: DeserializeOwned {
424 fn get_event_type(&self) -> StateEventType;
426
427 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent;
429
430 fn get_event_id(&self) -> Option<&EventId>;
432
433 fn get_sender(&self) -> &UserId;
435
436 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch>;
438}
439
440impl AnyStateEventEnum for AnySyncStateEvent {
441 fn get_event_type(&self) -> StateEventType {
443 self.event_type()
444 }
445
446 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent {
447 self.content()
448 }
449
450 fn get_event_id(&self) -> Option<&EventId> {
451 Some(self.event_id())
452 }
453
454 fn get_sender(&self) -> &UserId {
455 self.sender()
456 }
457
458 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch> {
459 Some(self.origin_server_ts())
460 }
461}
462
463impl AnyStateEventEnum for AnyStrippedStateEvent {
464 fn get_event_type(&self) -> StateEventType {
466 self.event_type()
467 }
468
469 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent {
470 self.content()
471 }
472
473 fn get_event_id(&self) -> Option<&EventId> {
474 None
475 }
476
477 fn get_sender(&self) -> &UserId {
478 self.sender()
479 }
480
481 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch> {
482 None
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use ruma::{event_id, events::room::name::PossiblyRedactedRoomNameEventContent};
489
490 use super::MinimalStateEvent;
491
492 #[test]
493 fn test_backward_compatible_deserialize_minimal_state_event() {
494 let event_id = event_id!("$event");
495
496 let event =
498 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
499 r#"{"Original":{"content":{"name":"My Room"},"event_id":"$event"}}"#,
500 )
501 .unwrap();
502 assert_eq!(event.content.name.as_deref(), Some("My Room"));
503 assert_eq!(event.event_id.as_deref(), Some(event_id));
504
505 let event =
506 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
507 r#"{"Redacted":{"content":{},"event_id":"$event"}}"#,
508 )
509 .unwrap();
510 assert_eq!(event.content.name, None);
511 assert_eq!(event.event_id.as_deref(), Some(event_id));
512
513 let event =
515 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
516 r#"{"PossiblyRedacted":{"content":{"name":"My Room"},"event_id":"$event"}}"#,
517 )
518 .unwrap();
519 assert_eq!(event.content.name.as_deref(), Some("My Room"));
520 assert_eq!(event.event_id.as_deref(), Some(event_id));
521
522 let event =
523 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
524 r#"{"PossiblyRedacted":{"content":{},"event_id":"$event"}}"#,
525 )
526 .unwrap();
527 assert_eq!(event.content.name, None);
528 assert_eq!(event.event_id.as_deref(), Some(event_id));
529 }
530}