matrix_sdk_base/
utils.rs

1use ruma::{
2    EventId, OwnedEventId, assign,
3    events::{
4        RedactContent, RedactedStateEventContent, StateEventContent, StaticStateEventContent,
5        SyncStateEvent,
6        room::{
7            avatar::{RoomAvatarEventContent, StrippedRoomAvatarEvent},
8            canonical_alias::{RoomCanonicalAliasEventContent, StrippedRoomCanonicalAliasEvent},
9            create::{StrippedRoomCreateEvent, SyncRoomCreateEvent},
10            guest_access::{
11                RedactedRoomGuestAccessEventContent, RoomGuestAccessEventContent,
12                StrippedRoomGuestAccessEvent,
13            },
14            history_visibility::{
15                RoomHistoryVisibilityEventContent, StrippedRoomHistoryVisibilityEvent,
16            },
17            join_rules::{RoomJoinRulesEventContent, StrippedRoomJoinRulesEvent},
18            member::{MembershipState, RoomMemberEventContent},
19            name::{RedactedRoomNameEventContent, RoomNameEventContent, StrippedRoomNameEvent},
20            tombstone::{
21                RedactedRoomTombstoneEventContent, RoomTombstoneEventContent,
22                StrippedRoomTombstoneEvent,
23            },
24            topic::{RedactedRoomTopicEventContent, RoomTopicEventContent, StrippedRoomTopicEvent},
25        },
26    },
27    room_version_rules::RedactionRules,
28};
29use serde::{Deserialize, Serialize, de::DeserializeOwned};
30
31use crate::room::RoomCreateWithCreatorEventContent;
32
33// #[serde(bound)] instead of DeserializeOwned in type where clause does not
34// work, it can only be a single bound that replaces the default and if a helper
35// trait is used, the compiler still complains about Deserialize not being
36// implemented for C::Redacted.
37//
38// It is unclear why a Serialize bound on C::Redacted is not also required.
39
40/// A minimal state event.
41///
42/// This type can hold a possibly-redacted state event with an optional
43/// event ID. The event ID is optional so this type can also hold events from
44/// invited rooms, where event IDs are not available.
45#[derive(Clone, Debug, Deserialize, Serialize)]
46#[serde(bound(
47    serialize = "C: Serialize, C::Redacted: Serialize",
48    deserialize = "C: DeserializeOwned, C::Redacted: DeserializeOwned"
49))]
50pub enum MinimalStateEvent<C: StateEventContent + RedactContent>
51where
52    C::Redacted: RedactedStateEventContent,
53{
54    /// An unredacted event.
55    Original(OriginalMinimalStateEvent<C>),
56    /// A redacted event.
57    Redacted(RedactedMinimalStateEvent<C::Redacted>),
58}
59
60/// An unredacted minimal state event.
61///
62/// For more details see [`MinimalStateEvent`].
63#[derive(Clone, Debug, Deserialize, Serialize)]
64pub struct OriginalMinimalStateEvent<C>
65where
66    C: StateEventContent,
67{
68    /// The event's content.
69    pub content: C,
70    /// The event's ID, if known.
71    pub event_id: Option<OwnedEventId>,
72}
73
74/// A redacted minimal state event.
75///
76/// For more details see [`MinimalStateEvent`].
77#[derive(Clone, Debug, Deserialize, Serialize)]
78pub struct RedactedMinimalStateEvent<C>
79where
80    C: RedactedStateEventContent,
81{
82    /// The event's content.
83    pub content: C,
84    /// The event's ID, if known.
85    pub event_id: Option<OwnedEventId>,
86}
87
88impl<C> MinimalStateEvent<C>
89where
90    C: StateEventContent + RedactContent,
91    C::Redacted: RedactedStateEventContent,
92{
93    /// Get the inner event's ID.
94    pub fn event_id(&self) -> Option<&EventId> {
95        match self {
96            MinimalStateEvent::Original(ev) => ev.event_id.as_deref(),
97            MinimalStateEvent::Redacted(ev) => ev.event_id.as_deref(),
98        }
99    }
100
101    /// Returns the inner event, if it isn't redacted.
102    pub fn as_original(&self) -> Option<&OriginalMinimalStateEvent<C>> {
103        match self {
104            MinimalStateEvent::Original(ev) => Some(ev),
105            MinimalStateEvent::Redacted(_) => None,
106        }
107    }
108
109    /// Converts `self` to the inner `OriginalMinimalStateEvent<C>`, if it isn't
110    /// redacted.
111    pub fn into_original(self) -> Option<OriginalMinimalStateEvent<C>> {
112        match self {
113            MinimalStateEvent::Original(ev) => Some(ev),
114            MinimalStateEvent::Redacted(_) => None,
115        }
116    }
117
118    /// Redacts this event.
119    ///
120    /// Does nothing if it is already redacted.
121    pub fn redact(&mut self, rules: &RedactionRules)
122    where
123        C: Clone,
124    {
125        if let MinimalStateEvent::Original(ev) = self {
126            *self = MinimalStateEvent::Redacted(RedactedMinimalStateEvent {
127                content: ev.content.clone().redact(rules),
128                event_id: ev.event_id.clone(),
129            });
130        }
131    }
132}
133
134/// A minimal `m.room.member` event.
135pub type MinimalRoomMemberEvent = MinimalStateEvent<RoomMemberEventContent>;
136
137impl MinimalRoomMemberEvent {
138    /// Obtain the membership state, regardless of whether this event is
139    /// redacted.
140    pub fn membership(&self) -> &MembershipState {
141        match self {
142            MinimalStateEvent::Original(ev) => &ev.content.membership,
143            MinimalStateEvent::Redacted(ev) => &ev.content.membership,
144        }
145    }
146}
147
148impl<C> From<SyncStateEvent<C>> for MinimalStateEvent<C>
149where
150    C: StaticStateEventContent + RedactContent,
151    C::Redacted: RedactedStateEventContent,
152{
153    fn from(ev: SyncStateEvent<C>) -> Self {
154        match ev {
155            SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
156                content: ev.content,
157                event_id: Some(ev.event_id),
158            }),
159            SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
160                content: ev.content,
161                event_id: Some(ev.event_id),
162            }),
163        }
164    }
165}
166
167impl<C> From<&SyncStateEvent<C>> for MinimalStateEvent<C>
168where
169    C: Clone + StaticStateEventContent + RedactContent,
170    C::Redacted: Clone + RedactedStateEventContent,
171{
172    fn from(ev: &SyncStateEvent<C>) -> Self {
173        match ev {
174            SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
175                content: ev.content.clone(),
176                event_id: Some(ev.event_id.clone()),
177            }),
178            SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
179                content: ev.content.clone(),
180                event_id: Some(ev.event_id.clone()),
181            }),
182        }
183    }
184}
185
186impl From<&SyncRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
187    fn from(ev: &SyncRoomCreateEvent) -> Self {
188        match ev {
189            SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
190                content: RoomCreateWithCreatorEventContent::from_event_content(
191                    ev.content.clone(),
192                    ev.sender.clone(),
193                ),
194                event_id: Some(ev.event_id.clone()),
195            }),
196            SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
197                content: RoomCreateWithCreatorEventContent::from_event_content(
198                    ev.content.clone(),
199                    ev.sender.clone(),
200                ),
201                event_id: Some(ev.event_id.clone()),
202            }),
203        }
204    }
205}
206
207impl From<&StrippedRoomAvatarEvent> for MinimalStateEvent<RoomAvatarEventContent> {
208    fn from(event: &StrippedRoomAvatarEvent) -> Self {
209        let content = assign!(RoomAvatarEventContent::new(), {
210            info: event.content.info.clone(),
211            url: event.content.url.clone(),
212        });
213        // event might actually be redacted, there is no way to tell for
214        // stripped state events.
215        Self::Original(OriginalMinimalStateEvent { content, event_id: None })
216    }
217}
218
219impl From<&StrippedRoomNameEvent> for MinimalStateEvent<RoomNameEventContent> {
220    fn from(event: &StrippedRoomNameEvent) -> Self {
221        match event.content.name.clone() {
222            Some(name) => {
223                let content = RoomNameEventContent::new(name);
224                Self::Original(OriginalMinimalStateEvent { content, event_id: None })
225            }
226            None => {
227                let content = RedactedRoomNameEventContent::new();
228                Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
229            }
230        }
231    }
232}
233
234impl From<&StrippedRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
235    fn from(event: &StrippedRoomCreateEvent) -> Self {
236        let content = RoomCreateWithCreatorEventContent::from_event_content(
237            event.content.clone(),
238            event.sender.clone(),
239        );
240        Self::Original(OriginalMinimalStateEvent { content, event_id: None })
241    }
242}
243
244impl From<&StrippedRoomHistoryVisibilityEvent>
245    for MinimalStateEvent<RoomHistoryVisibilityEventContent>
246{
247    fn from(event: &StrippedRoomHistoryVisibilityEvent) -> Self {
248        let content =
249            RoomHistoryVisibilityEventContent::new(event.content.history_visibility.clone());
250        Self::Original(OriginalMinimalStateEvent { content, event_id: None })
251    }
252}
253
254impl From<&StrippedRoomGuestAccessEvent> for MinimalStateEvent<RoomGuestAccessEventContent> {
255    fn from(event: &StrippedRoomGuestAccessEvent) -> Self {
256        match &event.content.guest_access {
257            Some(guest_access) => {
258                let content = RoomGuestAccessEventContent::new(guest_access.clone());
259                Self::Original(OriginalMinimalStateEvent { content, event_id: None })
260            }
261            None => {
262                let content = RedactedRoomGuestAccessEventContent::new();
263                Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
264            }
265        }
266    }
267}
268
269impl From<&StrippedRoomJoinRulesEvent> for MinimalStateEvent<RoomJoinRulesEventContent> {
270    fn from(event: &StrippedRoomJoinRulesEvent) -> Self {
271        let content = RoomJoinRulesEventContent::new(event.content.join_rule.clone());
272        Self::Original(OriginalMinimalStateEvent { content, event_id: None })
273    }
274}
275
276impl From<&StrippedRoomCanonicalAliasEvent> for MinimalStateEvent<RoomCanonicalAliasEventContent> {
277    fn from(event: &StrippedRoomCanonicalAliasEvent) -> Self {
278        let content = assign!(RoomCanonicalAliasEventContent::new(), {
279            alias: event.content.alias.clone(),
280            alt_aliases: event.content.alt_aliases.clone(),
281        });
282        Self::Original(OriginalMinimalStateEvent { content, event_id: None })
283    }
284}
285
286impl From<&StrippedRoomTopicEvent> for MinimalStateEvent<RoomTopicEventContent> {
287    fn from(event: &StrippedRoomTopicEvent) -> Self {
288        match &event.content.topic {
289            Some(topic) => {
290                let content = RoomTopicEventContent::new(topic.clone());
291                Self::Original(OriginalMinimalStateEvent { content, event_id: None })
292            }
293            None => {
294                let content = RedactedRoomTopicEventContent::new();
295                Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
296            }
297        }
298    }
299}
300
301impl From<&StrippedRoomTombstoneEvent> for MinimalStateEvent<RoomTombstoneEventContent> {
302    fn from(event: &StrippedRoomTombstoneEvent) -> Self {
303        match (&event.content.body, &event.content.replacement_room) {
304            (Some(body), Some(replacement_room)) => {
305                let content =
306                    RoomTombstoneEventContent::new(body.clone(), replacement_room.clone());
307                Self::Original(OriginalMinimalStateEvent { content, event_id: None })
308            }
309            _ => {
310                let content = RedactedRoomTombstoneEventContent::new();
311                Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
312            }
313        }
314    }
315}