1use ruma::{
2 EventId, OwnedEventId, assign,
3 events::{
4 AnySyncStateEvent, AnySyncTimelineEvent, RedactContent, RedactedStateEventContent,
5 StateEventContent, StateEventType, StaticEventContent, StaticStateEventContent,
6 SyncStateEvent,
7 room::{
8 avatar::{RoomAvatarEventContent, StrippedRoomAvatarEvent},
9 canonical_alias::{RoomCanonicalAliasEventContent, StrippedRoomCanonicalAliasEvent},
10 create::{StrippedRoomCreateEvent, SyncRoomCreateEvent},
11 guest_access::{
12 RedactedRoomGuestAccessEventContent, RoomGuestAccessEventContent,
13 StrippedRoomGuestAccessEvent,
14 },
15 history_visibility::{
16 RoomHistoryVisibilityEventContent, StrippedRoomHistoryVisibilityEvent,
17 },
18 join_rules::{RoomJoinRulesEventContent, StrippedRoomJoinRulesEvent},
19 member::{MembershipState, RoomMemberEventContent},
20 name::{RedactedRoomNameEventContent, RoomNameEventContent, StrippedRoomNameEvent},
21 tombstone::{
22 RedactedRoomTombstoneEventContent, RoomTombstoneEventContent,
23 StrippedRoomTombstoneEvent,
24 },
25 topic::{RedactedRoomTopicEventContent, RoomTopicEventContent, StrippedRoomTopicEvent},
26 },
27 },
28 room_version_rules::RedactionRules,
29 serde::Raw,
30};
31use serde::{Deserialize, Serialize, de::DeserializeOwned};
32use tracing::{error, warn};
33
34use crate::room::RoomCreateWithCreatorEventContent;
35
36#[derive(Clone, Debug, Deserialize, Serialize)]
49#[serde(bound(
50 serialize = "C: Serialize, C::Redacted: Serialize",
51 deserialize = "C: DeserializeOwned, C::Redacted: DeserializeOwned"
52))]
53pub enum MinimalStateEvent<C: StateEventContent + RedactContent>
54where
55 C::Redacted: RedactedStateEventContent,
56{
57 Original(OriginalMinimalStateEvent<C>),
59 Redacted(RedactedMinimalStateEvent<C::Redacted>),
61}
62
63#[derive(Clone, Debug, Deserialize, Serialize)]
67pub struct OriginalMinimalStateEvent<C>
68where
69 C: StateEventContent,
70{
71 pub content: C,
73 pub event_id: Option<OwnedEventId>,
75}
76
77#[derive(Clone, Debug, Deserialize, Serialize)]
81pub struct RedactedMinimalStateEvent<C>
82where
83 C: RedactedStateEventContent,
84{
85 pub content: C,
87 pub event_id: Option<OwnedEventId>,
89}
90
91impl<C> MinimalStateEvent<C>
92where
93 C: StateEventContent + RedactContent,
94 C::Redacted: RedactedStateEventContent,
95{
96 pub fn event_id(&self) -> Option<&EventId> {
98 match self {
99 MinimalStateEvent::Original(ev) => ev.event_id.as_deref(),
100 MinimalStateEvent::Redacted(ev) => ev.event_id.as_deref(),
101 }
102 }
103
104 pub fn as_original(&self) -> Option<&OriginalMinimalStateEvent<C>> {
106 match self {
107 MinimalStateEvent::Original(ev) => Some(ev),
108 MinimalStateEvent::Redacted(_) => None,
109 }
110 }
111
112 pub fn into_original(self) -> Option<OriginalMinimalStateEvent<C>> {
115 match self {
116 MinimalStateEvent::Original(ev) => Some(ev),
117 MinimalStateEvent::Redacted(_) => None,
118 }
119 }
120
121 pub fn redact(&mut self, rules: &RedactionRules)
125 where
126 C: Clone,
127 {
128 if let MinimalStateEvent::Original(ev) = self {
129 *self = MinimalStateEvent::Redacted(RedactedMinimalStateEvent {
130 content: ev.content.clone().redact(rules),
131 event_id: ev.event_id.clone(),
132 });
133 }
134 }
135}
136
137pub type MinimalRoomMemberEvent = MinimalStateEvent<RoomMemberEventContent>;
139
140impl MinimalRoomMemberEvent {
141 pub fn membership(&self) -> &MembershipState {
144 match self {
145 MinimalStateEvent::Original(ev) => &ev.content.membership,
146 MinimalStateEvent::Redacted(ev) => &ev.content.membership,
147 }
148 }
149}
150
151impl<C> From<SyncStateEvent<C>> for MinimalStateEvent<C>
152where
153 C: StaticStateEventContent + RedactContent,
154 C::Redacted: RedactedStateEventContent,
155{
156 fn from(ev: SyncStateEvent<C>) -> Self {
157 match ev {
158 SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
159 content: ev.content,
160 event_id: Some(ev.event_id),
161 }),
162 SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
163 content: ev.content,
164 event_id: Some(ev.event_id),
165 }),
166 }
167 }
168}
169
170impl<C> From<&SyncStateEvent<C>> for MinimalStateEvent<C>
171where
172 C: Clone + StaticStateEventContent + RedactContent,
173 C::Redacted: Clone + RedactedStateEventContent,
174{
175 fn from(ev: &SyncStateEvent<C>) -> Self {
176 match ev {
177 SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
178 content: ev.content.clone(),
179 event_id: Some(ev.event_id.clone()),
180 }),
181 SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
182 content: ev.content.clone(),
183 event_id: Some(ev.event_id.clone()),
184 }),
185 }
186 }
187}
188
189impl From<&SyncRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
190 fn from(ev: &SyncRoomCreateEvent) -> Self {
191 match ev {
192 SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent {
193 content: RoomCreateWithCreatorEventContent::from_event_content(
194 ev.content.clone(),
195 ev.sender.clone(),
196 ),
197 event_id: Some(ev.event_id.clone()),
198 }),
199 SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent {
200 content: RoomCreateWithCreatorEventContent::from_event_content(
201 ev.content.clone(),
202 ev.sender.clone(),
203 ),
204 event_id: Some(ev.event_id.clone()),
205 }),
206 }
207 }
208}
209
210impl From<&StrippedRoomAvatarEvent> for MinimalStateEvent<RoomAvatarEventContent> {
211 fn from(event: &StrippedRoomAvatarEvent) -> Self {
212 let content = assign!(RoomAvatarEventContent::new(), {
213 info: event.content.info.clone(),
214 url: event.content.url.clone(),
215 });
216 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
219 }
220}
221
222impl From<&StrippedRoomNameEvent> for MinimalStateEvent<RoomNameEventContent> {
223 fn from(event: &StrippedRoomNameEvent) -> Self {
224 match event.content.name.clone() {
225 Some(name) => {
226 let content = RoomNameEventContent::new(name);
227 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
228 }
229 None => {
230 let content = RedactedRoomNameEventContent::new();
231 Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
232 }
233 }
234 }
235}
236
237impl From<&StrippedRoomCreateEvent> for MinimalStateEvent<RoomCreateWithCreatorEventContent> {
238 fn from(event: &StrippedRoomCreateEvent) -> Self {
239 let content = RoomCreateWithCreatorEventContent::from_event_content(
240 event.content.clone(),
241 event.sender.clone(),
242 );
243 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
244 }
245}
246
247impl From<&StrippedRoomHistoryVisibilityEvent>
248 for MinimalStateEvent<RoomHistoryVisibilityEventContent>
249{
250 fn from(event: &StrippedRoomHistoryVisibilityEvent) -> Self {
251 let content =
252 RoomHistoryVisibilityEventContent::new(event.content.history_visibility.clone());
253 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
254 }
255}
256
257impl From<&StrippedRoomGuestAccessEvent> for MinimalStateEvent<RoomGuestAccessEventContent> {
258 fn from(event: &StrippedRoomGuestAccessEvent) -> Self {
259 match &event.content.guest_access {
260 Some(guest_access) => {
261 let content = RoomGuestAccessEventContent::new(guest_access.clone());
262 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
263 }
264 None => {
265 let content = RedactedRoomGuestAccessEventContent::new();
266 Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
267 }
268 }
269 }
270}
271
272impl From<&StrippedRoomJoinRulesEvent> for MinimalStateEvent<RoomJoinRulesEventContent> {
273 fn from(event: &StrippedRoomJoinRulesEvent) -> Self {
274 let content = RoomJoinRulesEventContent::new(event.content.join_rule.clone());
275 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
276 }
277}
278
279impl From<&StrippedRoomCanonicalAliasEvent> for MinimalStateEvent<RoomCanonicalAliasEventContent> {
280 fn from(event: &StrippedRoomCanonicalAliasEvent) -> Self {
281 let content = assign!(RoomCanonicalAliasEventContent::new(), {
282 alias: event.content.alias.clone(),
283 alt_aliases: event.content.alt_aliases.clone(),
284 });
285 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
286 }
287}
288
289impl From<&StrippedRoomTopicEvent> for MinimalStateEvent<RoomTopicEventContent> {
290 fn from(event: &StrippedRoomTopicEvent) -> Self {
291 match &event.content.topic {
292 Some(topic) => {
293 let content = RoomTopicEventContent::new(topic.clone());
294 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
295 }
296 None => {
297 let content = RedactedRoomTopicEventContent::new();
298 Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
299 }
300 }
301 }
302}
303
304impl From<&StrippedRoomTombstoneEvent> for MinimalStateEvent<RoomTombstoneEventContent> {
305 fn from(event: &StrippedRoomTombstoneEvent) -> Self {
306 match (&event.content.body, &event.content.replacement_room) {
307 (Some(body), Some(replacement_room)) => {
308 let content =
309 RoomTombstoneEventContent::new(body.clone(), replacement_room.clone());
310 Self::Original(OriginalMinimalStateEvent { content, event_id: None })
311 }
312 _ => {
313 let content = RedactedRoomTombstoneEventContent::new();
314 Self::Redacted(RedactedMinimalStateEvent { content, event_id: None })
315 }
316 }
317 }
318}
319
320#[derive(Debug, Clone)]
326pub struct RawSyncStateEventWithKeys {
327 pub raw: Raw<AnySyncStateEvent>,
329 pub event_type: StateEventType,
331 pub state_key: String,
333 cached_event: Option<Result<AnySyncStateEvent, ()>>,
335}
336
337impl RawSyncStateEventWithKeys {
338 pub fn try_from_raw_state_event(raw: Raw<AnySyncStateEvent>) -> Option<Self> {
343 let StateEventWithKeysDeHelper { event_type, state_key } =
344 match raw.deserialize_as_unchecked() {
345 Ok(fields) => fields,
346 Err(error) => {
347 warn!(?error, "Couldn't deserialize type and state key of state event");
348 return None;
349 }
350 };
351
352 let Some(state_key) = state_key else {
354 warn!(
355 ?event_type,
356 "Couldn't deserialize type and state key of state event: missing state key"
357 );
358 return None;
359 };
360
361 Some(Self { raw, event_type, state_key, cached_event: None })
362 }
363
364 pub fn try_from_raw_timeline_event(raw: &Raw<AnySyncTimelineEvent>) -> Option<Self> {
370 let StateEventWithKeysDeHelper { event_type, state_key } = match raw
371 .deserialize_as_unchecked()
372 {
373 Ok(fields) => fields,
374 Err(error) => {
375 warn!(?error, "Couldn't deserialize type and optional state key of timeline event");
376 return None;
377 }
378 };
379
380 Some(Self {
382 event_type,
383 state_key: state_key?,
384 raw: raw.clone().cast_unchecked(),
385 cached_event: None,
386 })
387 }
388
389 pub fn deserialize_as<F, C>(&mut self, as_variant_fn: F) -> Option<&SyncStateEvent<C>>
403 where
404 F: FnOnce(&AnySyncStateEvent) -> Option<&SyncStateEvent<C>>,
405 C: StaticEventContent + StaticStateEventContent + RedactContent,
406 C::Redacted: RedactedStateEventContent,
407 {
408 let any_event = self
409 .cached_event
410 .get_or_insert_with(|| {
411 self.raw.deserialize().map_err(|error| {
412 warn!(event_type = ?C::TYPE, ?error, "Couldn't deserialize state event");
413 })
414 })
415 .as_ref()
416 .ok()?;
417
418 let event = as_variant_fn(any_event);
419
420 if event.is_none() {
421 error!(
423 expected_event_type = ?C::TYPE,
424 actual_event_type = ?any_event.event_type().to_string(),
425 "Couldn't deserialize state event: unexpected type",
426 );
427 }
428
429 event
430 }
431
432 pub(crate) fn set_cached_event(&mut self, event: AnySyncStateEvent) {
439 self.cached_event = Some(Ok(event));
440 }
441}
442
443#[derive(Deserialize)]
445struct StateEventWithKeysDeHelper {
446 #[serde(rename = "type")]
447 event_type: StateEventType,
448 state_key: Option<String>,
451}