logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
use std::fmt;

use serde::{de::DeserializeOwned, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::serde::Raw;

use super::{
    EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
    RoomAccountDataEventType, StateEventType, ToDeviceEventType,
};

/// The base trait that all event content types implement.
///
/// Use [`macros::EventContent`] to derive this traits. It is not meant to be implemented manually.
///
/// [`macros::EventContent`]: super::macros::EventContent
pub trait EventContent: Sized + Serialize {
    /// The Rust enum for the event kind's known types.
    type EventType;

    /// Get the event's type, like `m.room.message`.
    fn event_type(&self) -> Self::EventType;

    /// Constructs the given event content.
    #[doc(hidden)]
    fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
}

impl<T> Raw<T>
where
    T: EventContent,
    T::EventType: fmt::Display,
{
    /// Try to deserialize the JSON as an event's content.
    pub fn deserialize_content(&self, event_type: T::EventType) -> serde_json::Result<T> {
        T::from_parts(&event_type.to_string(), self.json())
    }
}

/// The base trait that all redacted event content types implement.
///
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts.
/// The `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent {
    /// Constructs the redacted event content.
    ///
    /// If called for anything but "empty" redacted content this will error.
    #[doc(hidden)]
    fn empty(_event_type: &str) -> serde_json::Result<Self> {
        Err(serde::de::Error::custom("this event is not redacted"))
    }

    /// Determines if the redacted event content needs to serialize fields.
    #[doc(hidden)]
    fn has_serialize_fields(&self) -> bool;

    /// Determines if the redacted event content needs to deserialize fields.
    #[doc(hidden)]
    fn has_deserialize_fields() -> HasDeserializeFields;
}

/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
    /// Deserialize the event's content, failing if invalid.
    True,

    /// Return the redacted version of this event's content.
    False,

    /// `Optional` is used for `RedactedAliasesEventContent` since it has
    /// an empty version and one with content left after redaction that
    /// must be supported together.
    Optional,
}

/// Trait for abstracting over event content structs.
///
/// … but *not* enums which don't always have an event type and kind (e.g. message vs state) that's
/// fixed / known at compile time.
pub trait StaticEventContent: EventContent {
    /// The event's "kind".
    ///
    /// See the type's documentation.
    const KIND: EventKind;

    /// The event type.
    const TYPE: &'static str;
}

/// The "kind" of an event.
///
/// This corresponds directly to the event content marker traits.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EventKind {
    /// Global account data event kind.
    GlobalAccountData,

    /// Room account data event kind.
    RoomAccountData,

    /// Ephemeral room event kind.
    EphemeralRoomData,

    /// Message-like event kind.
    ///
    /// Since redacted / non-redacted message-like events are used in the same places but have
    /// different sets of fields, these two variations are treated as two closely-related event
    /// kinds.
    MessageLike {
        /// Redacted variation?
        redacted: bool,
    },

    /// State event kind.
    ///
    /// Since redacted / non-redacted state events are used in the same places but have different
    /// sets of fields, these two variations are treated as two closely-related event kinds.
    State {
        /// Redacted variation?
        redacted: bool,
    },

    /// To-device event kind.
    ToDevice,

    /// Presence event kind.
    Presence,
}

macro_rules! trait_aliases {
    // need to use `,` instead of `+` because (1) path can't be followed by `+`
    // and (2) `+` can't be used as a separator since it's a repetition operator
    ($(
        $( #[doc = $docs:literal] )*
        trait $id:ident = $( $def:path ),+;
    )*) => {
        $(
            $( #[doc = $docs] )*
            pub trait $id: $($def+)+ {}
            impl<T: $($def+)+> $id for T {}
        )*
    }
}

trait_aliases! {
    /// An alias for `EventContent<EventType = GlobalAccountDataEventType>`.
    trait GlobalAccountDataEventContent = EventContent<EventType = GlobalAccountDataEventType>;

    /// An alias for `EventContent<EventType = RoomAccountDataEventType>`.
    trait RoomAccountDataEventContent = EventContent<EventType = RoomAccountDataEventType>;

    /// An alias for `EventContent<EventType = EphemeralRoomEventType>`.
    trait EphemeralRoomEventContent = EventContent<EventType = EphemeralRoomEventType>;

    /// An alias for `EventContent<EventType = MessageLikeEventType>`.
    trait MessageLikeEventContent = EventContent<EventType = MessageLikeEventType>;

    /// An alias for `MessageLikeEventContent + RedactedEventContent`.
    trait RedactedMessageLikeEventContent = MessageLikeEventContent, RedactedEventContent;

    /// An alias for `StateEventContent + RedactedEventContent`.
    trait RedactedStateEventContent = StateEventContent, RedactedEventContent;

    /// An alias for `EventContent<EventType = ToDeviceEventType>`.
    trait ToDeviceEventContent = EventContent<EventType = ToDeviceEventType>;
}

/// An alias for `EventContent<EventType = StateEventType>`.
pub trait StateEventContent: EventContent<EventType = StateEventType> {
    /// The type of the event's `state_key` field.
    type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}