atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
crates/matrix-sdk-base/src/utils.rsRust530 lines17.9 KB
1
use 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
};
16
use serde::{Deserialize, Serialize, de::DeserializeOwned};
17
use tracing::{error, warn};
18
19
use crate::room::RoomCreateWithCreatorEventContent;
20
21
/// A minimal state event.
22
///
23
/// This type can hold a possibly-redacted state event with an optional
24
/// event ID. The event ID is optional so this type can also hold events from
25
/// invited rooms, where event IDs are not available.
26
#[derive(Clone, Debug, Deserialize, Serialize)]
27
#[serde(
28
    bound(serialize = "C: Serialize + Clone"),
29
    from = "MinimalStateEventSerdeHelper<C>",
30
    into = "MinimalStateEventSerdeHelper<C>"
31
)]
32
pub struct MinimalStateEvent<C: PossiblyRedactedStateEventContent + RedactContent> {
33
    /// The event's content.
34
    pub content: C,
35
    /// The event's ID, if known.
36
    pub event_id: Option<OwnedEventId>,
37
}
38
39
impl<C> MinimalStateEvent<C>
40
where
41
    C: PossiblyRedactedStateEventContent + RedactContent,
42
    C::Redacted: Into<C>,
43
{
44
    /// Redacts this event.
45
    ///
46
    /// Does nothing if it is already redacted.
47
    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
/// Helper type to (de)serialize [`MinimalStateEvent`].
56
#[derive(Serialize, Deserialize)]
57
enum MinimalStateEventSerdeHelper<C> {
58
    /// Previous variant for a non-redacted event.
59
    Original(MinimalStateEventSerdeHelperInner<C>),
60
    /// Previous variant for a redacted event.
61
    Redacted(MinimalStateEventSerdeHelperInner<C>),
62
    /// New variant.
63
    PossiblyRedacted(MinimalStateEventSerdeHelperInner<C>),
64
}
65
66
impl<C> From<MinimalStateEventSerdeHelper<C>> for MinimalStateEvent<C>
67
where
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
80
impl<C> From<MinimalStateEvent<C>> for MinimalStateEventSerdeHelper<C>
81
where
82
    C: PossiblyRedactedStateEventContent + RedactContent,
83
{
84
    fn from(value: MinimalStateEvent<C>) -> Self {
85
        Self::PossiblyRedacted(value.into())
86
    }
87
}
88
89
#[derive(Serialize, Deserialize)]
90
struct MinimalStateEventSerdeHelperInner<C> {
91
    content: C,
92
    event_id: Option<OwnedEventId>,
93
}
94
95
impl<C> From<MinimalStateEventSerdeHelperInner<C>> for MinimalStateEvent<C>
96
where
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
105
impl<C> From<MinimalStateEvent<C>> for MinimalStateEventSerdeHelperInner<C>
106
where
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
115
/// A minimal `m.room.member` event.
116
pub type MinimalRoomMemberEvent = MinimalStateEvent<PossiblyRedactedRoomMemberEventContent>;
117
118
impl<C1, C2> From<SyncStateEvent<C1>> for MinimalStateEvent<C2>
119
where
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
136
impl<C1, C2> From<&SyncStateEvent<C1>> for MinimalStateEvent<C2>
137
where
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
154
impl 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
175
impl<C> From<StrippedStateEvent<C>> for MinimalStateEvent<C>
176
where
177
    C: PossiblyRedactedStateEventContent + RedactContent,
178
{
179
    fn from(event: StrippedStateEvent<C>) -> Self {
180
        Self { content: event.content, event_id: None }
181
    }
182
}
183
184
impl<C> From<&StrippedStateEvent<C>> for MinimalStateEvent<C>
185
where
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
193
impl 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
/// A raw state event and its `(type, state_key)` tuple that identifies it
204
/// in the state map of the room.
205
///
206
/// This type can also cache the deserialized event lazily when using
207
/// [`RawStateEventWithKeys::deserialize_as()`].
208
#[derive(Debug, Clone)]
209
pub struct RawStateEventWithKeys<T: AnyStateEventEnum> {
210
    /// The raw state event.
211
    pub raw: Raw<T>,
212
    /// The type of the state event.
213
    pub event_type: StateEventType,
214
    /// The state key of the state event.
215
    pub state_key: String,
216
    /// The cached deserialized event.
217
    cached_event: Option<Result<T, ()>>,
218
}
219
220
impl<T: AnyStateEventEnum> RawStateEventWithKeys<T> {
221
    /// Try to construct a `RawStateEventWithKeys` from the given raw state
222
    /// event.
223
    ///
224
    /// Returns `None` if extracting the `type` or `state_key` fails.
225
    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
        // It should be a state event, so log if there is no state key.
236
        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
    /// Try to deserialize the raw event.
248
    ///
249
    /// The result of the event deserialization is cached for future calls to
250
    /// this method.
251
    ///
252
    /// Returns `None` if the deserialization failed.
253
    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
    /// Try to deserialize the raw event and return it as a
265
    /// [`MinimalStateEvent`] using the selected variant of
266
    /// [`AnyPossiblyRedactedStateEventContent`].
267
    ///
268
    /// This method should only be called if the variant is already known. It is
269
    /// considered a developer error for `as_variant_fn` to return `None`, but
270
    /// this API was chosen to simplify closures that use the
271
    /// [`as_variant!`](as_variant::as_variant) macro.
272
    ///
273
    /// The result of the event deserialization is cached for future calls to
274
    /// this method.
275
    ///
276
    /// Returns `None` if the deserialization failed or if `as_variant_fn`
277
    /// returns `None`.
278
    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
            // This should be a developer error, or an upstream error.
291
            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
    /// Override the event cached by
306
    /// [`RawStateEventWithKeys::deserialize_as()`].
307
    ///
308
    /// When validating the content of the deserialized event, this can be used
309
    /// to edit the parts that fail validation and pass the edited event down
310
    /// the chain.
311
    pub(crate) fn set_cached_event(&mut self, event: T) {
312
        self.cached_event = Some(Ok(event));
313
    }
314
}
315
316
impl RawStateEventWithKeys<AnySyncStateEvent> {
317
    /// Try to construct a `RawStateEventWithKeys` from the given raw
318
    /// timeline event.
319
    ///
320
    /// Returns `None` if deserializing the `type` or `state_key` fails, or if
321
    /// the event is not a state event.
322
    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
        // If the state key is missing, it is not a state event according to the spec.
334
        Some(Self {
335
            event_type,
336
            state_key: state_key?,
337
            raw: raw.clone().cast_unchecked(),
338
            cached_event: None,
339
        })
340
    }
341
342
    /// Try to deserialize the raw event and return the selected variant of
343
    /// [`AnySyncStateEvent`].
344
    ///
345
    /// This method should only be called if the variant is already known. It is
346
    /// considered a developer error for `as_variant_fn` to return `None`, but
347
    /// this API was chosen to simplify closures that use the
348
    /// [`as_variant!`](as_variant::as_variant) macro.
349
    ///
350
    /// The result of the event deserialization is cached for future calls to
351
    /// this method.
352
    ///
353
    /// Returns `None` if the deserialization failed or if `as_variant_fn`
354
    /// returns `None`.
355
    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
            // This should be a developer error, or an upstream error.
366
            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
377
impl RawStateEventWithKeys<AnyStrippedStateEvent> {
378
    /// Try to deserialize the raw event and return the selected variant of
379
    /// [`AnyStrippedStateEvent`].
380
    ///
381
    /// This method should only be called if the variant is already known. It is
382
    /// considered a developer error for `as_variant_fn` to return `None`, but
383
    /// this API was chosen to simplify closures that use the
384
    /// [`as_variant!`](as_variant::as_variant) macro.
385
    ///
386
    /// The result of the event deserialization is cached for future calls to
387
    /// this method.
388
    ///
389
    /// Returns `None` if the deserialization failed or if `as_variant_fn`
390
    /// returns `None`.
391
    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
            // This should be a developer error, or an upstream error.
401
            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
/// Helper type to deserialize a [`RawStateEventWithKeys`].
413
#[derive(Deserialize)]
414
struct StateEventWithKeysDeHelper {
415
    #[serde(rename = "type")]
416
    event_type: StateEventType,
417
    /// The state key is optional to be able to differentiate state events from
418
    /// other messages in the timeline.
419
    state_key: Option<String>,
420
}
421
422
/// Helper trait to use common methods of `Any*StateEvent` enums.
423
pub trait AnyStateEventEnum: DeserializeOwned {
424
    /// Get the type of the state event.
425
    fn get_event_type(&self) -> StateEventType;
426
427
    /// Get the content of the state event.
428
    fn get_content(&self) -> AnyPossiblyRedactedStateEventContent;
429
430
    /// Get the ID of the state event, if any.
431
    fn get_event_id(&self) -> Option<&EventId>;
432
433
    /// Get the sender of the state event.
434
    fn get_sender(&self) -> &UserId;
435
436
    /// Get the timestamp of the state event, if any.
437
    fn get_origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch>;
438
}
439
440
impl AnyStateEventEnum for AnySyncStateEvent {
441
    /// Get the type of the state event.
442
    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
463
impl AnyStateEventEnum for AnyStrippedStateEvent {
464
    /// Get the type of the state event.
465
    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)]
487
mod 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
        // The old format with `Original` and `Redacted` variants works.
497
        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
        // The new format works.
514
        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
}