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 {
336 event_type,
337 state_key: state_key?,
338 raw: raw.clone().cast_unchecked(),
339 cached_event: None,
340 })
341 }
342
343 pub fn deserialize_as<F, C>(&mut self, as_variant_fn: F) -> Option<&SyncStateEvent<C>>
357 where
358 F: FnOnce(&AnySyncStateEvent) -> Option<&SyncStateEvent<C>>,
359 C: StaticEventContent + StaticStateEventContent + RedactContent,
360 C::Redacted: RedactedStateEventContent,
361 {
362 let any_event = self.deserialize()?;
363 let event = as_variant_fn(any_event);
364
365 if event.is_none() {
366 error!(
368 expected_event_type = ?C::TYPE,
369 actual_event_type = ?any_event.event_type().to_string(),
370 "Couldn't deserialize state event: unexpected type",
371 );
372 }
373
374 event
375 }
376}
377
378impl RawStateEventWithKeys<AnyStrippedStateEvent> {
379 pub fn deserialize_as<F, C>(&mut self, as_variant_fn: F) -> Option<&StrippedStateEvent<C>>
393 where
394 F: FnOnce(&AnyStrippedStateEvent) -> Option<&StrippedStateEvent<C>>,
395 C: StaticEventContent + PossiblyRedactedStateEventContent,
396 {
397 let any_event = self.deserialize()?;
398 let event = as_variant_fn(any_event);
399
400 if event.is_none() {
401 error!(
403 expected_event_type = ?C::TYPE,
404 actual_event_type = ?any_event.event_type().to_string(),
405 "Couldn't deserialize stripped state event: unexpected type",
406 );
407 }
408
409 event
410 }
411}
412
413#[derive(Deserialize)]
415struct StateEventWithKeysDeHelper {
416 #[serde(rename = "type")]
417 event_type: StateEventType,
418 state_key: Option<String>,
421}
422
423pub trait AnyStateEventEnum: DeserializeOwned {
425 fn get_event_type(&self) -> StateEventType;
427
428 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent;
430
431 fn get_event_id(&self) -> Option<&EventId>;
433
434 fn get_sender(&self) -> &UserId;
436
437 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch>;
439}
440
441impl AnyStateEventEnum for AnySyncStateEvent {
442 fn get_event_type(&self) -> StateEventType {
444 self.event_type()
445 }
446
447 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent {
448 self.content()
449 }
450
451 fn get_event_id(&self) -> Option<&EventId> {
452 Some(self.event_id())
453 }
454
455 fn get_sender(&self) -> &UserId {
456 self.sender()
457 }
458
459 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch> {
460 Some(self.origin_server_ts())
461 }
462}
463
464impl AnyStateEventEnum for AnyStrippedStateEvent {
465 fn get_event_type(&self) -> StateEventType {
467 self.event_type()
468 }
469
470 fn get_content(&self) -> AnyPossiblyRedactedStateEventContent {
471 self.content()
472 }
473
474 fn get_event_id(&self) -> Option<&EventId> {
475 None
476 }
477
478 fn get_sender(&self) -> &UserId {
479 self.sender()
480 }
481
482 fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch> {
483 None
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use ruma::{event_id, events::room::name::PossiblyRedactedRoomNameEventContent};
490
491 use super::MinimalStateEvent;
492
493 #[test]
494 fn test_backward_compatible_deserialize_minimal_state_event() {
495 let event_id = event_id!("$event");
496
497 let event =
499 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
500 r#"{"Original":{"content":{"name":"My Room"},"event_id":"$event"}}"#,
501 )
502 .unwrap();
503 assert_eq!(event.content.name.as_deref(), Some("My Room"));
504 assert_eq!(event.event_id.as_deref(), Some(event_id));
505
506 let event =
507 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
508 r#"{"Redacted":{"content":{},"event_id":"$event"}}"#,
509 )
510 .unwrap();
511 assert_eq!(event.content.name, None);
512 assert_eq!(event.event_id.as_deref(), Some(event_id));
513
514 let event =
516 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
517 r#"{"PossiblyRedacted":{"content":{"name":"My Room"},"event_id":"$event"}}"#,
518 )
519 .unwrap();
520 assert_eq!(event.content.name.as_deref(), Some("My Room"));
521 assert_eq!(event.event_id.as_deref(), Some(event_id));
522
523 let event =
524 serde_json::from_str::<MinimalStateEvent<PossiblyRedactedRoomNameEventContent>>(
525 r#"{"PossiblyRedacted":{"content":{},"event_id":"$event"}}"#,
526 )
527 .unwrap();
528 assert_eq!(event.content.name, None);
529 assert_eq!(event.event_id.as_deref(), Some(event_id));
530 }
531}