Skip to main content

ruma_events/
kinds.rs

1use as_variant::as_variant;
2use ruma_common::{
3    EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
4    encryption::DeviceKeys,
5    room_version_rules::RedactionRules,
6    serde::{JsonCastable, JsonObject, Raw, from_raw_json_value},
7};
8use ruma_macros::Event;
9use serde::{Deserialize, Deserializer, Serialize, ser::SerializeStruct};
10use serde_json::value::RawValue as RawJsonValue;
11
12use super::{
13    AnyInitialStateEvent, EmptyStateKey, EphemeralRoomEventContent, EventContentFromType,
14    GlobalAccountDataEventContent, MessageLikeEventContent, MessageLikeEventType,
15    MessageLikeUnsigned, PossiblyRedactedStateEventContent, RedactContent,
16    RedactedMessageLikeEventContent, RedactedStateEventContent, RedactedUnsigned,
17    RedactionDeHelper, RoomAccountDataEventContent, StateEventType, StaticStateEventContent,
18    ToDeviceEventContent,
19};
20#[cfg(feature = "unstable-msc4354")]
21use crate::sticky::StickyObject;
22
23/// A global account data event.
24#[derive(Clone, Debug, Event)]
25#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
26pub struct GlobalAccountDataEvent<C: GlobalAccountDataEventContent> {
27    /// Data specific to the event type.
28    pub content: C,
29}
30
31impl<C: GlobalAccountDataEventContent> GlobalAccountDataEvent<C> {
32    /// Construct a new `GlobalAccountDataEvent` with the given content.
33    pub fn new(content: C) -> Self {
34        Self { content }
35    }
36}
37
38impl<C: GlobalAccountDataEventContent> Serialize for GlobalAccountDataEvent<C> {
39    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40    where
41        S: serde::Serializer,
42    {
43        let mut state = serializer.serialize_struct("GlobalAccountDataEvent", 2)?;
44        state.serialize_field("type", &self.content.event_type())?;
45        state.serialize_field("content", &self.content)?;
46        state.end()
47    }
48}
49
50impl<C: GlobalAccountDataEventContent> JsonCastable<JsonObject> for GlobalAccountDataEvent<C> {}
51
52/// A room account data event.
53#[derive(Clone, Debug, Event)]
54#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
55pub struct RoomAccountDataEvent<C: RoomAccountDataEventContent> {
56    /// Data specific to the event type.
57    pub content: C,
58}
59
60impl<C: RoomAccountDataEventContent> RoomAccountDataEvent<C> {
61    /// Construct a new `RoomAccountDataEvent` with the given content.
62    pub fn new(content: C) -> Self {
63        Self { content }
64    }
65}
66
67impl<C: RoomAccountDataEventContent> Serialize for RoomAccountDataEvent<C> {
68    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69    where
70        S: serde::Serializer,
71    {
72        let mut state = serializer.serialize_struct("RoomAccountDataEvent", 2)?;
73        state.serialize_field("type", &self.content.event_type())?;
74        state.serialize_field("content", &self.content)?;
75        state.end()
76    }
77}
78
79impl<C: RoomAccountDataEventContent> JsonCastable<JsonObject> for RoomAccountDataEvent<C> {}
80
81/// An ephemeral room event.
82#[derive(Clone, Debug, Event)]
83#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
84pub struct EphemeralRoomEvent<C: EphemeralRoomEventContent> {
85    /// Data specific to the event type.
86    pub content: C,
87
88    /// The ID of the room associated with this event.
89    pub room_id: OwnedRoomId,
90}
91
92impl<C: EphemeralRoomEventContent> EphemeralRoomEvent<C> {
93    /// Construct a new `EphemeralRoomEvent` with the given content and room ID.
94    pub fn new(room_id: OwnedRoomId, content: C) -> Self {
95        Self { content, room_id }
96    }
97}
98
99impl<C: EphemeralRoomEventContent> Serialize for EphemeralRoomEvent<C> {
100    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
101    where
102        S: serde::Serializer,
103    {
104        let mut state = serializer.serialize_struct("EphemeralRoomEvent", 2)?;
105        state.serialize_field("type", &self.content.event_type())?;
106        state.serialize_field("content", &self.content)?;
107        state.serialize_field("room_id", &self.room_id)?;
108        state.end()
109    }
110}
111
112impl<C: EphemeralRoomEventContent> JsonCastable<SyncEphemeralRoomEvent<C>>
113    for EphemeralRoomEvent<C>
114{
115}
116
117impl<C: EphemeralRoomEventContent> JsonCastable<JsonObject> for EphemeralRoomEvent<C> {}
118
119/// An ephemeral room event without a `room_id`.
120#[derive(Clone, Debug, Event)]
121#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
122pub struct SyncEphemeralRoomEvent<C: EphemeralRoomEventContent> {
123    /// Data specific to the event type.
124    pub content: C,
125}
126
127impl<C: EphemeralRoomEventContent> SyncEphemeralRoomEvent<C> {
128    /// Construct a new `SyncEphemeralRoomEvent` with the given content and room ID.
129    pub fn new(content: C) -> Self {
130        Self { content }
131    }
132}
133
134impl<C: EphemeralRoomEventContent> Serialize for SyncEphemeralRoomEvent<C> {
135    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136    where
137        S: serde::Serializer,
138    {
139        let mut state = serializer.serialize_struct("SyncEphemeralRoomEvent", 2)?;
140        state.serialize_field("type", &self.content.event_type())?;
141        state.serialize_field("content", &self.content)?;
142        state.end()
143    }
144}
145
146impl<C: EphemeralRoomEventContent> JsonCastable<JsonObject> for SyncEphemeralRoomEvent<C> {}
147
148/// An unredacted message-like event.
149///
150/// `OriginalMessageLikeEvent` implements the comparison traits using only the `event_id` field, a
151/// sorted list would be sorted lexicographically based on the event's `EventId`.
152#[derive(Clone, Debug, Event)]
153#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
154pub struct OriginalMessageLikeEvent<C: MessageLikeEventContent> {
155    /// Data specific to the event type.
156    pub content: C,
157
158    /// The globally unique identifier for the event.
159    pub event_id: OwnedEventId,
160
161    /// The fully-qualified ID of the user who sent this event.
162    pub sender: OwnedUserId,
163
164    /// Timestamp on the originating homeserver when this event was sent.
165    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
166
167    /// The ID of the room associated with this event.
168    pub room_id: OwnedRoomId,
169
170    /// Additional key-value pairs not signed by the homeserver.
171    pub unsigned: MessageLikeUnsigned<C>,
172
173    /// Message events can be annotated with a new top-level sticky object,
174    /// which MUST have a duration_ms, which is the number of milliseconds for the event to be
175    /// sticky.
176    #[cfg(feature = "unstable-msc4354")]
177    #[ruma_event(default, default_on_error, rename = "msc4354_sticky")]
178    pub sticky: Option<StickyObject>,
179}
180
181impl<C: MessageLikeEventContent> JsonCastable<OriginalSyncMessageLikeEvent<C>>
182    for OriginalMessageLikeEvent<C>
183{
184}
185
186impl<C: MessageLikeEventContent + RedactContent> JsonCastable<MessageLikeEvent<C>>
187    for OriginalMessageLikeEvent<C>
188where
189    C::Redacted: RedactedMessageLikeEventContent,
190{
191}
192
193impl<C: MessageLikeEventContent + RedactContent> JsonCastable<SyncMessageLikeEvent<C>>
194    for OriginalMessageLikeEvent<C>
195where
196    C::Redacted: RedactedMessageLikeEventContent,
197{
198}
199
200impl<C: MessageLikeEventContent> JsonCastable<JsonObject> for OriginalMessageLikeEvent<C> {}
201
202/// An unredacted message-like event without a `room_id`.
203///
204/// `OriginalSyncMessageLikeEvent` implements the comparison traits using only the `event_id` field,
205/// a sorted list would be sorted lexicographically based on the event's `EventId`.
206#[derive(Clone, Debug, Event)]
207#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
208pub struct OriginalSyncMessageLikeEvent<C: MessageLikeEventContent> {
209    /// Data specific to the event type.
210    pub content: C,
211
212    /// The globally unique identifier for the event.
213    pub event_id: OwnedEventId,
214
215    /// The fully-qualified ID of the user who sent this event.
216    pub sender: OwnedUserId,
217
218    /// Timestamp on the originating homeserver when this event was sent.
219    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
220
221    /// Additional key-value pairs not signed by the homeserver.
222    pub unsigned: MessageLikeUnsigned<C>,
223
224    /// Message events can be annotated with a new top-level sticky object,
225    /// which MUST have a duration_ms, which is the number of milliseconds for the event to be
226    /// sticky.
227    #[cfg(feature = "unstable-msc4354")]
228    #[ruma_event(default, default_on_error, rename = "msc4354_sticky")]
229    pub sticky: Option<StickyObject>,
230}
231
232impl<C: MessageLikeEventContent + RedactContent> OriginalSyncMessageLikeEvent<C>
233where
234    C::Redacted: RedactedMessageLikeEventContent,
235{
236    pub(crate) fn into_maybe_redacted(self) -> SyncMessageLikeEvent<C> {
237        SyncMessageLikeEvent::Original(self)
238    }
239}
240
241impl<C: MessageLikeEventContent + RedactContent> JsonCastable<SyncMessageLikeEvent<C>>
242    for OriginalSyncMessageLikeEvent<C>
243where
244    C::Redacted: RedactedMessageLikeEventContent,
245{
246}
247
248impl<C: MessageLikeEventContent> JsonCastable<JsonObject> for OriginalSyncMessageLikeEvent<C> {}
249
250/// A redacted message-like event.
251///
252/// `RedactedMessageLikeEvent` implements the comparison traits using only the `event_id` field, a
253/// sorted list would be sorted lexicographically based on the event's `EventId`.
254#[derive(Clone, Debug, Event)]
255#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
256pub struct RedactedMessageLikeEvent<C: RedactedMessageLikeEventContent> {
257    /// Data specific to the event type.
258    pub content: C,
259
260    /// The globally unique identifier for the event.
261    pub event_id: OwnedEventId,
262
263    /// The fully-qualified ID of the user who sent this event.
264    pub sender: OwnedUserId,
265
266    /// Timestamp on the originating homeserver when this event was sent.
267    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
268
269    /// The ID of the room associated with this event.
270    pub room_id: OwnedRoomId,
271
272    /// Additional key-value pairs not signed by the homeserver.
273    pub unsigned: RedactedUnsigned,
274}
275
276impl<C: RedactedMessageLikeEventContent> JsonCastable<RedactedSyncMessageLikeEvent<C>>
277    for RedactedMessageLikeEvent<C>
278{
279}
280
281impl<C: MessageLikeEventContent + RedactContent> JsonCastable<MessageLikeEvent<C>>
282    for RedactedMessageLikeEvent<C::Redacted>
283where
284    C::Redacted: RedactedMessageLikeEventContent,
285{
286}
287
288impl<C: MessageLikeEventContent + RedactContent> JsonCastable<SyncMessageLikeEvent<C>>
289    for RedactedMessageLikeEvent<C::Redacted>
290where
291    C::Redacted: RedactedMessageLikeEventContent,
292{
293}
294
295impl<C: RedactedMessageLikeEventContent> JsonCastable<JsonObject> for RedactedMessageLikeEvent<C> {}
296
297/// A redacted message-like event without a `room_id`.
298///
299/// `RedactedSyncMessageLikeEvent` implements the comparison traits using only the `event_id` field,
300/// a sorted list would be sorted lexicographically based on the event's `EventId`.
301#[derive(Clone, Debug, Event)]
302#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
303pub struct RedactedSyncMessageLikeEvent<C: RedactedMessageLikeEventContent> {
304    /// Data specific to the event type.
305    pub content: C,
306
307    /// The globally unique identifier for the event.
308    pub event_id: OwnedEventId,
309
310    /// The fully-qualified ID of the user who sent this event.
311    pub sender: OwnedUserId,
312
313    /// Timestamp on the originating homeserver when this event was sent.
314    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
315
316    /// Additional key-value pairs not signed by the homeserver.
317    pub unsigned: RedactedUnsigned,
318}
319
320impl<C: MessageLikeEventContent + RedactContent> JsonCastable<SyncMessageLikeEvent<C>>
321    for RedactedSyncMessageLikeEvent<C::Redacted>
322where
323    C::Redacted: RedactedMessageLikeEventContent,
324{
325}
326
327impl<C: RedactedMessageLikeEventContent> JsonCastable<JsonObject>
328    for RedactedSyncMessageLikeEvent<C>
329{
330}
331
332/// A possibly-redacted message-like event.
333///
334/// `MessageLikeEvent` implements the comparison traits using only the `event_id` field, a sorted
335/// list would be sorted lexicographically based on the event's `EventId`.
336#[allow(clippy::exhaustive_enums)]
337#[derive(Clone, Debug)]
338pub enum MessageLikeEvent<C: MessageLikeEventContent + RedactContent>
339where
340    C::Redacted: RedactedMessageLikeEventContent,
341{
342    /// Original, unredacted form of the event.
343    Original(OriginalMessageLikeEvent<C>),
344
345    /// Redacted form of the event with minimal fields.
346    Redacted(RedactedMessageLikeEvent<C::Redacted>),
347}
348
349impl<C: MessageLikeEventContent + RedactContent> JsonCastable<SyncMessageLikeEvent<C>>
350    for MessageLikeEvent<C>
351where
352    C::Redacted: RedactedMessageLikeEventContent,
353{
354}
355
356impl<C: MessageLikeEventContent + RedactContent> JsonCastable<JsonObject> for MessageLikeEvent<C> where
357    C::Redacted: RedactedMessageLikeEventContent
358{
359}
360
361/// A possibly-redacted message-like event without a `room_id`.
362///
363/// `SyncMessageLikeEvent` implements the comparison traits using only the `event_id` field, a
364/// sorted list would be sorted lexicographically based on the event's `EventId`.
365#[allow(clippy::exhaustive_enums)]
366#[derive(Clone, Debug)]
367pub enum SyncMessageLikeEvent<C: MessageLikeEventContent + RedactContent>
368where
369    C::Redacted: RedactedMessageLikeEventContent,
370{
371    /// Original, unredacted form of the event.
372    Original(OriginalSyncMessageLikeEvent<C>),
373
374    /// Redacted form of the event with minimal fields.
375    Redacted(RedactedSyncMessageLikeEvent<C::Redacted>),
376}
377
378impl<C: MessageLikeEventContent + RedactContent> JsonCastable<JsonObject>
379    for SyncMessageLikeEvent<C>
380where
381    C::Redacted: RedactedMessageLikeEventContent,
382{
383}
384
385/// An unredacted state event.
386///
387/// `OriginalStateEvent` implements the comparison traits using only the `event_id` field, a sorted
388/// list would be sorted lexicographically based on the event's `EventId`.
389#[derive(Clone, Debug, Event)]
390#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
391pub struct OriginalStateEvent<C: StaticStateEventContent> {
392    /// Data specific to the event type.
393    pub content: C,
394
395    /// The globally unique identifier for the event.
396    pub event_id: OwnedEventId,
397
398    /// The fully-qualified ID of the user who sent this event.
399    pub sender: OwnedUserId,
400
401    /// Timestamp on the originating homeserver when this event was sent.
402    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
403
404    /// The ID of the room associated with this event.
405    pub room_id: OwnedRoomId,
406
407    /// A unique key which defines the overwriting semantics for this piece of room state.
408    ///
409    /// This must be a string type, and is often an empty string.
410    ///
411    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
412    /// the same tuple replaces the previous one.
413    pub state_key: C::StateKey,
414
415    /// Additional key-value pairs not signed by the homeserver.
416    pub unsigned: C::Unsigned,
417
418    /// Message events can be annotated with a new top-level sticky object,
419    /// which MUST have a duration_ms, which is the number of milliseconds for the event to be
420    /// sticky.
421    #[cfg(feature = "unstable-msc4354")]
422    #[ruma_event(default, default_on_error, rename = "msc4354_sticky")]
423    pub sticky: Option<StickyObject>,
424}
425
426impl<C: StaticStateEventContent> JsonCastable<OriginalSyncStateEvent<C>> for OriginalStateEvent<C> {}
427
428impl<C: StaticStateEventContent + RedactContent> JsonCastable<StateEvent<C>>
429    for OriginalStateEvent<C>
430where
431    C::Redacted: RedactedStateEventContent,
432{
433}
434
435impl<C: StaticStateEventContent + RedactContent> JsonCastable<SyncStateEvent<C>>
436    for OriginalStateEvent<C>
437where
438    C::Redacted: RedactedStateEventContent,
439{
440}
441
442impl<C: StaticStateEventContent> JsonCastable<StrippedStateEvent<C::PossiblyRedacted>>
443    for OriginalStateEvent<C>
444where
445    C::PossiblyRedacted: PossiblyRedactedStateEventContent,
446{
447}
448
449impl<C: StaticStateEventContent> JsonCastable<JsonObject> for OriginalStateEvent<C> {}
450
451/// An unredacted state event without a `room_id`.
452///
453/// `OriginalSyncStateEvent` implements the comparison traits using only the `event_id` field, a
454/// sorted list would be sorted lexicographically based on the event's `EventId`.
455#[derive(Clone, Debug, Event)]
456#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
457pub struct OriginalSyncStateEvent<C: StaticStateEventContent> {
458    /// Data specific to the event type.
459    pub content: C,
460
461    /// The globally unique identifier for the event.
462    pub event_id: OwnedEventId,
463
464    /// The fully-qualified ID of the user who sent this event.
465    pub sender: OwnedUserId,
466
467    /// Timestamp on the originating homeserver when this event was sent.
468    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
469
470    /// A unique key which defines the overwriting semantics for this piece of room state.
471    ///
472    /// This must be a string type, and is often an empty string.
473    ///
474    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
475    /// the same tuple replaces the previous one.
476    pub state_key: C::StateKey,
477
478    /// Additional key-value pairs not signed by the homeserver.
479    pub unsigned: C::Unsigned,
480
481    /// Message events can be annotated with a new top-level sticky object,
482    /// which MUST have a duration_ms, which is the number of milliseconds for the event to be
483    /// sticky.
484    #[cfg(feature = "unstable-msc4354")]
485    #[ruma_event(default, default_on_error, rename = "msc4354_sticky")]
486    pub sticky: Option<StickyObject>,
487}
488
489impl<C: StaticStateEventContent + RedactContent> JsonCastable<SyncStateEvent<C>>
490    for OriginalSyncStateEvent<C>
491where
492    C::Redacted: RedactedStateEventContent,
493{
494}
495
496impl<C: StaticStateEventContent> JsonCastable<StrippedStateEvent<C::PossiblyRedacted>>
497    for OriginalSyncStateEvent<C>
498where
499    C::PossiblyRedacted: PossiblyRedactedStateEventContent,
500{
501}
502
503impl<C: StaticStateEventContent> JsonCastable<JsonObject> for OriginalSyncStateEvent<C> {}
504
505/// A stripped-down state event, used for previews of rooms the user has been invited to.
506#[derive(Clone, Debug, Event)]
507#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
508pub struct StrippedStateEvent<C: PossiblyRedactedStateEventContent> {
509    /// Data specific to the event type.
510    pub content: C,
511
512    /// The fully-qualified ID of the user who sent this event.
513    pub sender: OwnedUserId,
514
515    /// A unique key which defines the overwriting semantics for this piece of room state.
516    ///
517    /// This must be a string type, and is often an empty string.
518    ///
519    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
520    /// the same tuple replaces the previous one.
521    pub state_key: C::StateKey,
522
523    /// Timestamp on the originating homeserver when this event was sent.
524    ///
525    /// This field is usually stripped, but some events might include it.
526    #[cfg(feature = "unstable-msc4319")]
527    #[ruma_event(default)]
528    pub origin_server_ts: Option<MilliSecondsSinceUnixEpoch>,
529
530    /// Additional key-value pairs not signed by the homeserver.
531    #[cfg(feature = "unstable-msc4319")]
532    pub unsigned: Option<Raw<crate::StateUnsigned<C>>>,
533}
534
535impl<C: PossiblyRedactedStateEventContent> JsonCastable<JsonObject> for StrippedStateEvent<C> {}
536
537/// A minimal state event, used for creating a new room.
538#[derive(Clone, Debug, Event)]
539#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
540pub struct InitialStateEvent<C: StaticStateEventContent> {
541    /// Data specific to the event type.
542    pub content: C,
543
544    /// A unique key which defines the overwriting semantics for this piece of room state.
545    ///
546    /// This must be a string type, and is often an empty string.
547    ///
548    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
549    /// the same tuple replaces the previous one.
550    ///
551    /// Defaults to the empty string.
552    pub state_key: C::StateKey,
553}
554
555impl<C: StaticStateEventContent> InitialStateEvent<C> {
556    /// Create a new `InitialStateEvent` for an event type with the given state key.
557    ///
558    /// For cases where the state key is empty,
559    /// [`with_empty_state_key()`](Self::with_empty_state_key) can be used instead.
560    pub fn new(state_key: C::StateKey, content: C) -> Self {
561        Self { content, state_key }
562    }
563
564    /// Create a new `InitialStateEvent` for an event type with an empty state key.
565    ///
566    /// For cases where the state key is not empty, use [`new()`](Self::new).
567    pub fn with_empty_state_key(content: C) -> Self
568    where
569        C: StaticStateEventContent<StateKey = EmptyStateKey>,
570    {
571        Self::new(EmptyStateKey, content)
572    }
573
574    /// Shorthand for `Raw::new(self).unwrap()`.
575    ///
576    /// Since none of the content types in Ruma ever return an error in serialization, this will
577    /// never panic with `C` being a type from Ruma. However, if you use a custom content type
578    /// with a `Serialize` implementation that can error (for example because it contains an
579    /// `enum` with one or more variants that use the `#[serde(skip)]` attribute), this method
580    /// can panic.
581    pub fn to_raw(&self) -> Raw<Self> {
582        Raw::new(self).unwrap()
583    }
584
585    /// Shorthand for `self.to_raw().cast::<AnyInitialStateEvent>()`.
586    ///
587    /// Since none of the content types in Ruma ever return an error in serialization, this will
588    /// never panic with `C` being a type from Ruma. However, if you use a custom content type
589    /// with a `Serialize` implementation that can error (for example because it contains an
590    /// `enum` with one or more variants that use the `#[serde(skip)]` attribute), this method
591    /// can panic.
592    pub fn to_raw_any(&self) -> Raw<AnyInitialStateEvent> {
593        self.to_raw().cast()
594    }
595}
596
597impl<C> Default for InitialStateEvent<C>
598where
599    C: StaticStateEventContent<StateKey = EmptyStateKey> + Default,
600{
601    fn default() -> Self {
602        Self { content: Default::default(), state_key: EmptyStateKey }
603    }
604}
605
606impl<C: StaticStateEventContent> Serialize for InitialStateEvent<C> {
607    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
608    where
609        S: serde::Serializer,
610    {
611        let mut state = serializer.serialize_struct("InitialStateEvent", 3)?;
612        state.serialize_field("type", &self.content.event_type())?;
613        state.serialize_field("content", &self.content)?;
614        state.serialize_field("state_key", &self.state_key)?;
615        state.end()
616    }
617}
618
619impl<C: StaticStateEventContent> JsonCastable<JsonObject> for InitialStateEvent<C> {}
620
621/// A redacted state event.
622///
623/// `RedactedStateEvent` implements the comparison traits using only the `event_id` field, a sorted
624/// list would be sorted lexicographically based on the event's `EventId`.
625#[derive(Clone, Debug, Event)]
626#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
627pub struct RedactedStateEvent<C: RedactedStateEventContent> {
628    /// Data specific to the event type.
629    pub content: C,
630
631    /// The globally unique identifier for the event.
632    pub event_id: OwnedEventId,
633
634    /// The fully-qualified ID of the user who sent this event.
635    pub sender: OwnedUserId,
636
637    /// Timestamp on the originating homeserver when this event was sent.
638    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
639
640    /// The ID of the room associated with this event.
641    pub room_id: OwnedRoomId,
642
643    /// A unique key which defines the overwriting semantics for this piece of room state.
644    ///
645    /// This must be a string type, and is often an empty string.
646    ///
647    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
648    /// the same tuple replaces the previous one.
649    pub state_key: C::StateKey,
650
651    /// Additional key-value pairs not signed by the homeserver.
652    pub unsigned: RedactedUnsigned,
653}
654
655impl<C: RedactedStateEventContent> JsonCastable<RedactedSyncStateEvent<C>>
656    for RedactedStateEvent<C>
657{
658}
659
660impl<C: StaticStateEventContent + RedactContent> JsonCastable<StateEvent<C>>
661    for RedactedStateEvent<C::Redacted>
662where
663    C::Redacted: RedactedStateEventContent,
664{
665}
666
667impl<C: StaticStateEventContent + RedactContent> JsonCastable<SyncStateEvent<C>>
668    for RedactedStateEvent<C::Redacted>
669where
670    C::Redacted: RedactedStateEventContent,
671{
672}
673
674impl<C: RedactedStateEventContent> JsonCastable<JsonObject> for RedactedStateEvent<C> {}
675
676/// A redacted state event without a `room_id`.
677///
678/// `RedactedSyncStateEvent` implements the comparison traits using only the `event_id` field, a
679/// sorted list would be sorted lexicographically based on the event's `EventId`.
680#[derive(Clone, Debug, Event)]
681#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
682pub struct RedactedSyncStateEvent<C: RedactedStateEventContent> {
683    /// Data specific to the event type.
684    pub content: C,
685
686    /// The globally unique identifier for the event.
687    pub event_id: OwnedEventId,
688
689    /// The fully-qualified ID of the user who sent this event.
690    pub sender: OwnedUserId,
691
692    /// Timestamp on the originating homeserver when this event was sent.
693    pub origin_server_ts: MilliSecondsSinceUnixEpoch,
694
695    /// A unique key which defines the overwriting semantics for this piece of room state.
696    ///
697    /// This must be a string type, and is often an empty string.
698    ///
699    /// A state event is keyed by its `(type, state_key)` tuple. Sending another state event with
700    /// the same tuple replaces the previous one.
701    pub state_key: C::StateKey,
702
703    /// Additional key-value pairs not signed by the homeserver.
704    pub unsigned: RedactedUnsigned,
705}
706
707impl<C: StaticStateEventContent + RedactContent> JsonCastable<SyncStateEvent<C>>
708    for RedactedSyncStateEvent<C::Redacted>
709where
710    C::Redacted: RedactedStateEventContent,
711{
712}
713
714impl<C: RedactedStateEventContent> JsonCastable<JsonObject> for RedactedSyncStateEvent<C> {}
715
716/// A possibly-redacted state event.
717///
718/// `StateEvent` implements the comparison traits using only the `event_id` field, a sorted list
719/// would be sorted lexicographically based on the event's `EventId`.
720#[allow(clippy::exhaustive_enums)]
721#[derive(Clone, Debug)]
722pub enum StateEvent<C: StaticStateEventContent + RedactContent>
723where
724    C::Redacted: RedactedStateEventContent,
725{
726    /// Original, unredacted form of the event.
727    Original(OriginalStateEvent<C>),
728
729    /// Redacted form of the event with minimal fields.
730    Redacted(RedactedStateEvent<C::Redacted>),
731}
732
733impl<C: StaticStateEventContent + RedactContent> JsonCastable<SyncStateEvent<C>> for StateEvent<C> where
734    C::Redacted: RedactedStateEventContent
735{
736}
737
738impl<C: StaticStateEventContent + RedactContent>
739    JsonCastable<StrippedStateEvent<C::PossiblyRedacted>> for StateEvent<C>
740where
741    C::Redacted: RedactedStateEventContent,
742    C::PossiblyRedacted: PossiblyRedactedStateEventContent,
743{
744}
745
746impl<C: StaticStateEventContent + RedactContent> JsonCastable<JsonObject> for StateEvent<C> where
747    C::Redacted: RedactedStateEventContent
748{
749}
750
751/// A possibly-redacted state event without a `room_id`.
752///
753/// `SyncStateEvent` implements the comparison traits using only the `event_id` field, a sorted list
754/// would be sorted lexicographically based on the event's `EventId`.
755#[allow(clippy::exhaustive_enums)]
756#[derive(Clone, Debug)]
757pub enum SyncStateEvent<C: StaticStateEventContent + RedactContent>
758where
759    C::Redacted: RedactedStateEventContent,
760{
761    /// Original, unredacted form of the event.
762    Original(OriginalSyncStateEvent<C>),
763
764    /// Redacted form of the event with minimal fields.
765    Redacted(RedactedSyncStateEvent<C::Redacted>),
766}
767
768impl<C: StaticStateEventContent + RedactContent>
769    JsonCastable<StrippedStateEvent<C::PossiblyRedacted>> for SyncStateEvent<C>
770where
771    C::Redacted: RedactedStateEventContent,
772    C::PossiblyRedacted: PossiblyRedactedStateEventContent,
773{
774}
775
776impl<C: StaticStateEventContent + RedactContent> JsonCastable<JsonObject> for SyncStateEvent<C> where
777    C::Redacted: RedactedStateEventContent
778{
779}
780
781/// An event sent using send-to-device messaging.
782#[derive(Clone, Debug, Event)]
783#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
784pub struct ToDeviceEvent<C: ToDeviceEventContent> {
785    /// Data specific to the event type.
786    pub content: C,
787
788    /// The fully-qualified ID of the user who sent this event.
789    pub sender: OwnedUserId,
790}
791
792impl<C: ToDeviceEventContent> ToDeviceEvent<C> {
793    /// Construct a new `ToDeviceEvent` with the given content and sender.
794    pub fn new(sender: OwnedUserId, content: C) -> Self {
795        Self { content, sender }
796    }
797}
798
799impl<C: ToDeviceEventContent> Serialize for ToDeviceEvent<C> {
800    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
801    where
802        S: serde::Serializer,
803    {
804        let mut state = serializer.serialize_struct("ToDeviceEvent", 3)?;
805        state.serialize_field("type", &self.content.event_type())?;
806        state.serialize_field("content", &self.content)?;
807        state.serialize_field("sender", &self.sender)?;
808        state.end()
809    }
810}
811
812impl<C: ToDeviceEventContent> JsonCastable<JsonObject> for ToDeviceEvent<C> {}
813
814/// The decrypted payload of an `m.olm.v1.curve25519-aes-sha2` event.
815#[derive(Clone, Debug, Event)]
816#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
817pub struct DecryptedOlmV1Event<C: MessageLikeEventContent> {
818    /// Data specific to the event type.
819    pub content: C,
820
821    /// The fully-qualified ID of the user who sent this event.
822    pub sender: OwnedUserId,
823
824    /// The fully-qualified ID of the intended recipient this event.
825    pub recipient: OwnedUserId,
826
827    /// The recipient's ed25519 key.
828    pub recipient_keys: OlmV1Keys,
829
830    /// The sender's ed25519 key.
831    pub keys: OlmV1Keys,
832
833    /// The sender's device keys.
834    pub sender_device_keys: Option<Raw<DeviceKeys>>,
835}
836
837/// Public keys used for an `m.olm.v1.curve25519-aes-sha2` event.
838#[derive(Clone, Debug, Deserialize, Serialize)]
839#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
840pub struct OlmV1Keys {
841    /// An ed25519 key.
842    pub ed25519: String,
843}
844
845impl OlmV1Keys {
846    /// Construct a new `OlmV1Keys` with the given ed25519 key.
847    pub fn new(ed25519: String) -> Self {
848        Self { ed25519 }
849    }
850}
851
852/// The decrypted payload of an `m.megolm.v1.aes-sha2` event.
853#[derive(Clone, Debug, Event)]
854#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
855pub struct DecryptedMegolmV1Event<C: MessageLikeEventContent> {
856    /// Data specific to the event type.
857    pub content: C,
858
859    /// The ID of the room associated with the event.
860    pub room_id: OwnedRoomId,
861}
862
863/// A possibly-redacted state event content and the corresponding previous content from the unsigned
864/// event data, if available.
865#[allow(clippy::exhaustive_enums)]
866#[derive(Clone, Debug)]
867pub enum StateEventContentChange<C: StaticStateEventContent + RedactContent> {
868    /// Original, unredacted content of the event.
869    Original {
870        /// Current content of the room state.
871        content: C,
872
873        /// Previous content of the room state.
874        prev_content: Option<C::PossiblyRedacted>,
875    },
876
877    /// Redacted content of the event.
878    Redacted(C::Redacted),
879}
880
881impl<C: StaticStateEventContent + RedactContent> StateEventContentChange<C>
882where
883    C::Redacted: RedactedStateEventContent,
884{
885    /// Get the event’s type, like `m.room.create`.
886    pub fn event_type(&self) -> StateEventType {
887        match self {
888            Self::Original { content, .. } => content.event_type(),
889            Self::Redacted(content) => content.event_type(),
890        }
891    }
892
893    /// Transform `self` into a redacted form (removing most or all fields) according to the spec.
894    ///
895    /// If `self` is already [`Redacted`](Self::Redacted), return the inner data unmodified.
896    ///
897    /// A small number of events have room-version specific redaction behavior, so a
898    /// [`RedactionRules`] has to be specified.
899    pub fn redact(self, rules: &RedactionRules) -> C::Redacted {
900        match self {
901            Self::Original { content, .. } => content.redact(rules),
902            Self::Redacted(content) => content,
903        }
904    }
905}
906
907macro_rules! impl_possibly_redacted_event {
908    (
909        $ty:ident ( $content_trait:ident, $redacted_content_trait:ident, $event_type:ident )
910        $( where C::Redacted: $trait:ident<StateKey = C::StateKey>, )?
911        { $($extra:tt)* }
912    ) => {
913        impl<C> $ty<C>
914        where
915            C: $content_trait + RedactContent,
916            C::Redacted: $redacted_content_trait,
917            $( C::Redacted: $trait<StateKey = C::StateKey>, )?
918        {
919            /// Returns the `type` of this event.
920            pub fn event_type(&self) -> $event_type {
921                match self {
922                    Self::Original(ev) => ev.content.event_type(),
923                    Self::Redacted(ev) => ev.content.event_type(),
924                }
925            }
926
927            /// Returns this event's `event_id` field.
928            pub fn event_id(&self) -> &EventId {
929                match self {
930                    Self::Original(ev) => &ev.event_id,
931                    Self::Redacted(ev) => &ev.event_id,
932                }
933            }
934
935            /// Returns this event's `sender` field.
936            pub fn sender(&self) -> &UserId {
937                match self {
938                    Self::Original(ev) => &ev.sender,
939                    Self::Redacted(ev) => &ev.sender,
940                }
941            }
942
943            /// Returns this event's `origin_server_ts` field.
944            pub fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
945                match self {
946                    Self::Original(ev) => ev.origin_server_ts,
947                    Self::Redacted(ev) => ev.origin_server_ts,
948                }
949            }
950
951            // So the room_id method can be in the same impl block, in rustdoc
952            $($extra)*
953        }
954
955        impl<'de, C> Deserialize<'de> for $ty<C>
956        where
957            C: $content_trait + EventContentFromType + RedactContent,
958            C::Redacted: $redacted_content_trait + EventContentFromType,
959            $( C::Redacted: $trait<StateKey = C::StateKey>, )?
960        {
961            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
962            where
963                D: Deserializer<'de>,
964            {
965                let json = Box::<RawJsonValue>::deserialize(deserializer)?;
966                let RedactionDeHelper { unsigned } = from_raw_json_value(&json)?;
967
968                if unsigned.and_then(|u| u.redacted_because).is_some() {
969                    Ok(Self::Redacted(from_raw_json_value(&json)?))
970                } else {
971                    Ok(Self::Original(from_raw_json_value(&json)?))
972                }
973            }
974        }
975    }
976}
977
978impl_possibly_redacted_event!(
979    MessageLikeEvent(
980        MessageLikeEventContent, RedactedMessageLikeEventContent, MessageLikeEventType
981    ) {
982        /// Returns this event's `room_id` field.
983        pub fn room_id(&self) -> &RoomId {
984            match self {
985                Self::Original(ev) => &ev.room_id,
986                Self::Redacted(ev) => &ev.room_id,
987            }
988        }
989
990        /// Get the inner `OriginalMessageLikeEvent` if this is an unredacted event.
991        pub fn as_original(&self) -> Option<&OriginalMessageLikeEvent<C>> {
992            as_variant!(self, Self::Original)
993        }
994    }
995);
996
997impl_possibly_redacted_event!(
998    SyncMessageLikeEvent(
999        MessageLikeEventContent, RedactedMessageLikeEventContent, MessageLikeEventType
1000    ) {
1001        /// Get the inner `OriginalSyncMessageLikeEvent` if this is an unredacted event.
1002        pub fn as_original(&self) -> Option<&OriginalSyncMessageLikeEvent<C>> {
1003            as_variant!(self, Self::Original)
1004        }
1005
1006        /// Convert this sync event into a full event (one with a `room_id` field).
1007        pub fn into_full_event(self, room_id: OwnedRoomId) -> MessageLikeEvent<C> {
1008            match self {
1009                Self::Original(ev) => MessageLikeEvent::Original(ev.into_full_event(room_id)),
1010                Self::Redacted(ev) => MessageLikeEvent::Redacted(ev.into_full_event(room_id)),
1011            }
1012        }
1013    }
1014);
1015
1016impl_possibly_redacted_event!(
1017    StateEvent(StaticStateEventContent, RedactedStateEventContent, StateEventType)
1018    where
1019        C::Redacted: RedactedStateEventContent<StateKey = C::StateKey>,
1020    {
1021        /// Returns this event's `room_id` field.
1022        pub fn room_id(&self) -> &RoomId {
1023            match self {
1024                Self::Original(ev) => &ev.room_id,
1025                Self::Redacted(ev) => &ev.room_id,
1026            }
1027        }
1028
1029        /// Returns this event's `state_key` field.
1030        pub fn state_key(&self) -> &C::StateKey {
1031            match self {
1032                Self::Original(ev) => &ev.state_key,
1033                Self::Redacted(ev) => &ev.state_key,
1034            }
1035        }
1036
1037        /// Get the inner `OriginalStateEvent` if this is an unredacted event.
1038        pub fn as_original(&self) -> Option<&OriginalStateEvent<C>> {
1039            as_variant!(self, Self::Original)
1040        }
1041    }
1042);
1043
1044impl_possibly_redacted_event!(
1045    SyncStateEvent(StaticStateEventContent, RedactedStateEventContent, StateEventType)
1046    where
1047        C::Redacted: RedactedStateEventContent<StateKey = C::StateKey>,
1048    {
1049        /// Returns this event's `state_key` field.
1050        pub fn state_key(&self) -> &C::StateKey {
1051            match self {
1052                Self::Original(ev) => &ev.state_key,
1053                Self::Redacted(ev) => &ev.state_key,
1054            }
1055        }
1056
1057        /// Get the inner `OriginalSyncStateEvent` if this is an unredacted event.
1058        pub fn as_original(&self) -> Option<&OriginalSyncStateEvent<C>> {
1059            as_variant!(self, Self::Original)
1060        }
1061
1062        /// Convert this sync event into a full event (one with a `room_id` field).
1063        pub fn into_full_event(self, room_id: OwnedRoomId) -> StateEvent<C> {
1064            match self {
1065                Self::Original(ev) => StateEvent::Original(ev.into_full_event(room_id)),
1066                Self::Redacted(ev) => StateEvent::Redacted(ev.into_full_event(room_id)),
1067            }
1068        }
1069    }
1070);
1071
1072macro_rules! impl_sync_from_full {
1073    ($ty:ident, $full:ident, $content_trait:ident, $redacted_content_trait: ident) => {
1074        impl<C> From<$full<C>> for $ty<C>
1075        where
1076            C: $content_trait + RedactContent,
1077            C::Redacted: $redacted_content_trait,
1078        {
1079            fn from(full: $full<C>) -> Self {
1080                match full {
1081                    $full::Original(ev) => Self::Original(ev.into()),
1082                    $full::Redacted(ev) => Self::Redacted(ev.into()),
1083                }
1084            }
1085        }
1086    };
1087}
1088
1089impl_sync_from_full!(
1090    SyncMessageLikeEvent,
1091    MessageLikeEvent,
1092    MessageLikeEventContent,
1093    RedactedMessageLikeEventContent
1094);
1095impl_sync_from_full!(
1096    SyncStateEvent,
1097    StateEvent,
1098    StaticStateEventContent,
1099    RedactedStateEventContent
1100);