matrix_sdk_base/
utils.rs

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