1use std::{collections::BTreeMap, fmt};
18
19use matrix_sdk_common::{
20 debug::DebugRawEvent,
21 deserialized_responses::{ProcessedToDeviceEvent, TimelineEvent},
22};
23pub use ruma::api::client::sync::sync_events::v3::{
24 InvitedRoom as InvitedRoomUpdate, KnockedRoom as KnockedRoomUpdate,
25};
26use ruma::{
27 OwnedEventId, OwnedRoomId,
28 api::client::sync::sync_events::UnreadNotificationsCount as RumaUnreadNotificationsCount,
29 events::{
30 AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncEphemeralRoomEvent,
31 AnySyncStateEvent, presence::PresenceEvent,
32 },
33 push::Action,
34 serde::Raw,
35};
36use serde::{Deserialize, Serialize};
37
38use crate::{
39 debug::{
40 DebugInvitedRoom, DebugKnockedRoom, DebugListOfProcessedToDeviceEvents,
41 DebugListOfRawEvents, DebugListOfRawEventsNoId,
42 },
43 deserialized_responses::{AmbiguityChange, RawAnySyncOrStrippedTimelineEvent},
44};
45
46#[derive(Clone, Default)]
51pub struct SyncResponse {
52 pub rooms: RoomUpdates,
54 pub presence: Vec<Raw<PresenceEvent>>,
56 pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>,
58 pub to_device: Vec<ProcessedToDeviceEvent>,
60 pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>,
62}
63
64#[cfg(not(tarpaulin_include))]
65impl fmt::Debug for SyncResponse {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 f.debug_struct("SyncResponse")
68 .field("rooms", &self.rooms)
69 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
70 .field("to_device", &DebugListOfProcessedToDeviceEvents(&self.to_device))
71 .field("notifications", &self.notifications)
72 .finish_non_exhaustive()
73 }
74}
75
76#[derive(Clone, Default)]
78pub struct RoomUpdates {
79 pub left: BTreeMap<OwnedRoomId, LeftRoomUpdate>,
81 pub joined: BTreeMap<OwnedRoomId, JoinedRoomUpdate>,
83 pub invited: BTreeMap<OwnedRoomId, InvitedRoomUpdate>,
85 pub knocked: BTreeMap<OwnedRoomId, KnockedRoomUpdate>,
87}
88
89impl RoomUpdates {
90 pub(crate) fn iter_all_room_ids(&self) -> impl Iterator<Item = &OwnedRoomId> {
94 self.left
95 .keys()
96 .chain(self.joined.keys())
97 .chain(self.invited.keys())
98 .chain(self.knocked.keys())
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use std::collections::BTreeMap;
105
106 use assert_matches::assert_matches;
107 use ruma::room_id;
108
109 use super::{
110 InvitedRoomUpdate, JoinedRoomUpdate, KnockedRoomUpdate, LeftRoomUpdate, RoomUpdates,
111 };
112
113 #[test]
114 fn test_room_updates_iter_all_room_ids() {
115 let room_id_0 = room_id!("!r0");
116 let room_id_1 = room_id!("!r1");
117 let room_id_2 = room_id!("!r2");
118 let room_id_3 = room_id!("!r3");
119 let room_id_4 = room_id!("!r4");
120 let room_id_5 = room_id!("!r5");
121 let room_id_6 = room_id!("!r6");
122 let room_id_7 = room_id!("!r7");
123 let room_updates = RoomUpdates {
124 left: {
125 let mut left = BTreeMap::new();
126 left.insert(room_id_0.to_owned(), LeftRoomUpdate::default());
127 left.insert(room_id_1.to_owned(), LeftRoomUpdate::default());
128 left
129 },
130 joined: {
131 let mut joined = BTreeMap::new();
132 joined.insert(room_id_2.to_owned(), JoinedRoomUpdate::default());
133 joined.insert(room_id_3.to_owned(), JoinedRoomUpdate::default());
134 joined
135 },
136 invited: {
137 let mut invited = BTreeMap::new();
138 invited.insert(room_id_4.to_owned(), InvitedRoomUpdate::default());
139 invited.insert(room_id_5.to_owned(), InvitedRoomUpdate::default());
140 invited
141 },
142 knocked: {
143 let mut knocked = BTreeMap::new();
144 knocked.insert(room_id_6.to_owned(), KnockedRoomUpdate::default());
145 knocked.insert(room_id_7.to_owned(), KnockedRoomUpdate::default());
146 knocked
147 },
148 };
149
150 let mut iter = room_updates.iter_all_room_ids();
151 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_0));
152 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_1));
153 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_2));
154 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_3));
155 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_4));
156 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_5));
157 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_6));
158 assert_matches!(iter.next(), Some(room_id) => assert_eq!(room_id, room_id_7));
159 assert!(iter.next().is_none());
160 }
161}
162
163#[cfg(not(tarpaulin_include))]
164impl fmt::Debug for RoomUpdates {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.debug_struct("RoomUpdates")
167 .field("left", &self.left)
168 .field("joined", &self.joined)
169 .field("invited", &DebugInvitedRoomUpdates(&self.invited))
170 .field("knocked", &DebugKnockedRoomUpdates(&self.knocked))
171 .finish()
172 }
173}
174
175#[derive(Clone, Default)]
177pub struct JoinedRoomUpdate {
178 pub unread_notifications: UnreadNotificationsCount,
180 pub timeline: Timeline,
182 pub state: State,
193 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
195 pub ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
198 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
203}
204
205#[cfg(not(tarpaulin_include))]
206impl fmt::Debug for JoinedRoomUpdate {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_struct("JoinedRoomUpdate")
209 .field("unread_notifications", &self.unread_notifications)
210 .field("timeline", &self.timeline)
211 .field("state", &self.state)
212 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
213 .field("ephemeral", &self.ephemeral)
214 .field("ambiguity_changes", &self.ambiguity_changes)
215 .finish()
216 }
217}
218
219impl JoinedRoomUpdate {
220 pub(crate) fn new(
221 timeline: Timeline,
222 state: State,
223 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
224 ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
225 unread_notifications: UnreadNotificationsCount,
226 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
227 ) -> Self {
228 Self { unread_notifications, timeline, state, account_data, ephemeral, ambiguity_changes }
229 }
230}
231
232#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
234pub struct UnreadNotificationsCount {
235 pub highlight_count: u64,
238 pub notification_count: u64,
240}
241
242impl From<RumaUnreadNotificationsCount> for UnreadNotificationsCount {
243 fn from(notifications: RumaUnreadNotificationsCount) -> Self {
244 Self {
245 highlight_count: notifications.highlight_count.map(|c| c.into()).unwrap_or(0),
246 notification_count: notifications.notification_count.map(|c| c.into()).unwrap_or(0),
247 }
248 }
249}
250
251#[derive(Clone, Default)]
253pub struct LeftRoomUpdate {
254 pub timeline: Timeline,
257 pub state: State,
268 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
270 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
275}
276
277impl LeftRoomUpdate {
278 pub(crate) fn new(
279 timeline: Timeline,
280 state: State,
281 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
282 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
283 ) -> Self {
284 Self { timeline, state, account_data, ambiguity_changes }
285 }
286}
287
288#[cfg(not(tarpaulin_include))]
289impl fmt::Debug for LeftRoomUpdate {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 f.debug_struct("LeftRoomUpdate")
292 .field("timeline", &self.timeline)
293 .field("state", &self.state)
294 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
295 .field("ambiguity_changes", &self.ambiguity_changes)
296 .finish()
297 }
298}
299
300#[derive(Clone, Debug, Default)]
302pub struct Timeline {
303 pub limited: bool,
306
307 pub prev_batch: Option<String>,
310
311 pub events: Vec<TimelineEvent>,
313}
314
315impl Timeline {
316 pub(crate) fn new(limited: bool, prev_batch: Option<String>) -> Self {
317 Self { limited, prev_batch, ..Default::default() }
318 }
319}
320
321#[derive(Clone)]
323pub enum State {
324 Before(Vec<Raw<AnySyncStateEvent>>),
331
332 After(Vec<Raw<AnySyncStateEvent>>),
337}
338
339impl Default for State {
340 fn default() -> Self {
341 Self::Before(vec![])
342 }
343}
344
345#[cfg(not(tarpaulin_include))]
346impl fmt::Debug for State {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 match self {
349 Self::Before(events) => {
350 f.debug_tuple("Before").field(&DebugListOfRawEvents(events)).finish()
351 }
352 Self::After(events) => {
353 f.debug_tuple("After").field(&DebugListOfRawEvents(events)).finish()
354 }
355 }
356 }
357}
358
359struct DebugInvitedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, InvitedRoomUpdate>);
360
361#[cfg(not(tarpaulin_include))]
362impl fmt::Debug for DebugInvitedRoomUpdates<'_> {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugInvitedRoom(v)))).finish()
365 }
366}
367
368struct DebugKnockedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, KnockedRoomUpdate>);
369
370#[cfg(not(tarpaulin_include))]
371impl fmt::Debug for DebugKnockedRoomUpdates<'_> {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugKnockedRoom(v)))).finish()
374 }
375}
376
377#[derive(Clone)]
379pub struct Notification {
380 pub actions: Vec<Action>,
382
383 pub event: RawAnySyncOrStrippedTimelineEvent,
385}
386
387#[cfg(not(tarpaulin_include))]
388impl fmt::Debug for Notification {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 let event_debug = match &self.event {
391 RawAnySyncOrStrippedTimelineEvent::Sync(ev) => DebugRawEvent(ev),
392 RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => {
393 DebugRawEvent(ev.cast_ref_unchecked())
394 }
395 };
396
397 f.debug_struct("Notification")
398 .field("actions", &self.actions)
399 .field("event", &event_debug)
400 .finish()
401 }
402}