1use js_int::UInt;
6use ruma_common::{
7 serde::{from_raw_json_value, JsonCastable, JsonObject},
8 EventId, MilliSecondsSinceUnixEpoch, OwnedUserId, UserId,
9};
10use ruma_events::{
11 AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, OriginalStateEvent,
12 OriginalSyncStateEvent, PossiblyRedactedStateEventContent, RedactContent, RedactedStateEvent,
13 RedactedStateEventContent, RedactedSyncStateEvent, StateEvent, StateEventType,
14 StaticStateEventContent, StrippedStateEvent, SyncStateEvent,
15};
16use serde::{Deserialize, Serialize};
17use serde_json::value::RawValue as RawJsonValue;
18
19pub mod v3;
20
21#[cfg(feature = "unstable-msc4186")]
22pub mod v5;
23
24#[derive(Clone, Debug, Default, Deserialize, Serialize)]
26#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
27pub struct UnreadNotificationsCount {
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub highlight_count: Option<UInt>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub notification_count: Option<UInt>,
35}
36
37impl UnreadNotificationsCount {
38 pub fn new() -> Self {
40 Default::default()
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.highlight_count.is_none() && self.notification_count.is_none()
46 }
47}
48
49#[derive(Clone, Debug, Default, Deserialize, Serialize)]
51#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
52pub struct DeviceLists {
53 #[serde(default, skip_serializing_if = "Vec::is_empty")]
56 pub changed: Vec<OwnedUserId>,
57
58 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub left: Vec<OwnedUserId>,
62}
63
64impl DeviceLists {
65 pub fn new() -> Self {
67 Default::default()
68 }
69
70 pub fn is_empty(&self) -> bool {
72 self.changed.is_empty() && self.left.is_empty()
73 }
74}
75
76#[derive(Debug, Clone)]
78#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
79#[allow(clippy::large_enum_variant)]
80pub enum StrippedState {
81 Stripped(AnyStrippedStateEvent),
83
84 #[cfg(feature = "unstable-msc4319")]
86 Sync(AnySyncStateEvent),
87
88 #[cfg(feature = "unstable-msc4311")]
90 Full(AnyStateEvent),
91}
92
93impl StrippedState {
94 pub fn event_type(&self) -> StateEventType {
96 match self {
97 Self::Stripped(event) => event.event_type(),
98 #[cfg(feature = "unstable-msc4319")]
99 Self::Sync(event) => event.event_type(),
100 #[cfg(feature = "unstable-msc4311")]
101 Self::Full(event) => event.event_type(),
102 }
103 }
104
105 pub fn sender(&self) -> &UserId {
107 match self {
108 Self::Stripped(event) => event.sender(),
109 #[cfg(feature = "unstable-msc4319")]
110 Self::Sync(event) => event.sender(),
111 #[cfg(feature = "unstable-msc4311")]
112 Self::Full(event) => event.sender(),
113 }
114 }
115
116 pub fn state_key(&self) -> &str {
118 match self {
119 Self::Stripped(event) => event.state_key(),
120 #[cfg(feature = "unstable-msc4319")]
121 Self::Sync(event) => event.state_key(),
122 #[cfg(feature = "unstable-msc4311")]
123 Self::Full(event) => event.state_key(),
124 }
125 }
126
127 pub fn event_id(&self) -> Option<&EventId> {
129 match self {
130 Self::Stripped(_) => None,
131 #[cfg(feature = "unstable-msc4319")]
132 Self::Sync(event) => Some(event.event_id()),
133 #[cfg(feature = "unstable-msc4311")]
134 Self::Full(event) => Some(event.event_id()),
135 }
136 }
137
138 pub fn origin_server_ts(&self) -> Option<MilliSecondsSinceUnixEpoch> {
140 match self {
141 Self::Stripped(_) => None,
142 #[cfg(feature = "unstable-msc4319")]
143 Self::Sync(event) => Some(event.origin_server_ts()),
144 #[cfg(feature = "unstable-msc4311")]
145 Self::Full(event) => Some(event.origin_server_ts()),
146 }
147 }
148}
149
150impl<'de> Deserialize<'de> for StrippedState {
151 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152 where
153 D: serde::Deserializer<'de>,
154 {
155 let json = Box::<RawJsonValue>::deserialize(deserializer)?;
156
157 #[cfg(any(feature = "unstable-msc4311", feature = "unstable-msc4319"))]
158 {
159 use serde::de;
160
161 #[derive(Deserialize)]
162 struct StrippedStateDeHelper {
163 event_id: Option<de::IgnoredAny>,
164 origin_server_ts: Option<de::IgnoredAny>,
165 #[cfg(feature = "unstable-msc4311")]
166 room_id: Option<de::IgnoredAny>,
167 }
168
169 let StrippedStateDeHelper {
170 event_id,
171 origin_server_ts,
172 #[cfg(feature = "unstable-msc4311")]
173 room_id,
174 } = from_raw_json_value(&json)?;
175
176 if event_id.is_some() && origin_server_ts.is_some() {
177 #[cfg(feature = "unstable-msc4311")]
178 if room_id.is_some() {
179 return from_raw_json_value(&json).map(Self::Full);
180 }
181
182 #[cfg(feature = "unstable-msc4319")]
183 return from_raw_json_value(&json).map(Self::Sync);
184 }
185 }
186
187 from_raw_json_value(&json).map(Self::Stripped)
188 }
189}
190
191impl JsonCastable<StrippedState> for AnySyncStateEvent {}
192
193impl JsonCastable<StrippedState> for AnyStrippedStateEvent {}
194
195impl JsonCastable<AnyStrippedStateEvent> for StrippedState {}
196
197impl JsonCastable<StrippedState> for AnyStateEvent {}
198
199impl<C> JsonCastable<StrippedState> for OriginalStateEvent<C> where C: StaticStateEventContent {}
200
201impl<C> JsonCastable<StrippedState> for OriginalSyncStateEvent<C> where C: StaticStateEventContent {}
202
203impl<C> JsonCastable<StrippedState> for RedactedStateEvent<C> where C: RedactedStateEventContent {}
204
205impl<C> JsonCastable<StrippedState> for RedactedSyncStateEvent<C> where C: RedactedStateEventContent {}
206
207impl<C> JsonCastable<StrippedState> for StateEvent<C>
208where
209 C: StaticStateEventContent + RedactContent,
210 <C as RedactContent>::Redacted: RedactedStateEventContent,
211{
212}
213
214impl<C> JsonCastable<StrippedState> for SyncStateEvent<C>
215where
216 C: StaticStateEventContent + RedactContent,
217 <C as RedactContent>::Redacted: RedactedStateEventContent,
218{
219}
220
221impl<C> JsonCastable<StrippedState> for StrippedStateEvent<C> where
222 C: PossiblyRedactedStateEventContent
223{
224}
225
226impl JsonCastable<JsonObject> for StrippedState {}
227
228#[cfg(test)]
229mod tests {
230 use assert_matches2::assert_matches;
231 use ruma_common::user_id;
232 use ruma_events::{room::member::MembershipState, AnyStrippedStateEvent};
233 use serde_json::{from_value as from_json_value, json};
234
235 use crate::sync::sync_events::StrippedState;
236
237 #[test]
238 fn deserialize_stripped_state() {
239 let user_id = user_id!("@patrick:localhost");
240 let content = json!({
241 "membership": "join",
242 });
243
244 let stripped_event_json = json!({
246 "content": content,
247 "type": "m.room.member",
248 "state_key": user_id,
249 "sender": user_id,
250 });
251 assert_matches!(
252 from_json_value::<StrippedState>(stripped_event_json).unwrap(),
253 StrippedState::Stripped(AnyStrippedStateEvent::RoomMember(stripped_member_event))
254 );
255 assert_eq!(stripped_member_event.sender, user_id);
256 assert_eq!(stripped_member_event.state_key, user_id);
257 assert_eq!(stripped_member_event.content.membership, MembershipState::Join);
258
259 #[cfg(feature = "unstable-msc4319")]
260 {
261 use js_int::uint;
262 use ruma_common::event_id;
263 use ruma_events::{AnySyncStateEvent, SyncStateEvent};
264
265 let event_id = event_id!("$abcdefgh");
266
267 let sync_event_json = json!({
269 "content": content,
270 "event_id": event_id,
271 "origin_server_ts": 1_000_000,
272 "sender": user_id,
273 "state_key": user_id,
274 "type": "m.room.member",
275 });
276 assert_matches!(
277 from_json_value::<StrippedState>(sync_event_json).unwrap(),
278 StrippedState::Sync(AnySyncStateEvent::RoomMember(SyncStateEvent::Original(
279 sync_member_event
280 )))
281 );
282 assert_eq!(sync_member_event.content.membership, MembershipState::Join);
283 assert_eq!(sync_member_event.event_id, event_id);
284 assert_eq!(sync_member_event.origin_server_ts.0, uint!(1_000_000));
285 assert_eq!(sync_member_event.sender, user_id);
286 assert_eq!(sync_member_event.state_key, user_id);
287 }
288
289 #[cfg(feature = "unstable-msc4311")]
290 {
291 use js_int::uint;
292 use ruma_common::{event_id, room_id};
293 use ruma_events::{AnyStateEvent, StateEvent};
294
295 let event_id = event_id!("$abcdefgh");
296 let room_id = room_id!("!room:localhost");
297
298 let timeline_event_json = json!({
300 "content": content,
301 "event_id": event_id,
302 "origin_server_ts": 1_000_000,
303 "room_id": room_id,
304 "sender": user_id,
305 "state_key": user_id,
306 "type": "m.room.member",
307 });
308 assert_matches!(
309 from_json_value::<StrippedState>(timeline_event_json).unwrap(),
310 StrippedState::Full(AnyStateEvent::RoomMember(StateEvent::Original(
311 timeline_member_event
312 )))
313 );
314 assert_eq!(timeline_member_event.content.membership, MembershipState::Join);
315 assert_eq!(timeline_member_event.event_id, event_id);
316 assert_eq!(timeline_member_event.origin_server_ts.0, uint!(1_000_000));
317 assert_eq!(timeline_member_event.room_id, room_id);
318 assert_eq!(timeline_member_event.sender, user_id);
319 assert_eq!(timeline_member_event.state_key, user_id);
320 }
321 }
322}