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 api::client::sync::sync_events::UnreadNotificationsCount as RumaUnreadNotificationsCount,
28 events::{
29 presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent,
30 AnySyncEphemeralRoomEvent, AnySyncStateEvent,
31 },
32 push::Action,
33 serde::Raw,
34 OwnedEventId, OwnedRoomId,
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: Vec<Raw<AnySyncStateEvent>>,
187 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
189 pub ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
192 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
197}
198
199#[cfg(not(tarpaulin_include))]
200impl fmt::Debug for JoinedRoomUpdate {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 f.debug_struct("JoinedRoomUpdate")
203 .field("unread_notifications", &self.unread_notifications)
204 .field("timeline", &self.timeline)
205 .field("state", &DebugListOfRawEvents(&self.state))
206 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
207 .field("ephemeral", &self.ephemeral)
208 .field("ambiguity_changes", &self.ambiguity_changes)
209 .finish()
210 }
211}
212
213impl JoinedRoomUpdate {
214 pub(crate) fn new(
215 timeline: Timeline,
216 state: Vec<Raw<AnySyncStateEvent>>,
217 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
218 ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
219 unread_notifications: UnreadNotificationsCount,
220 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
221 ) -> Self {
222 Self { unread_notifications, timeline, state, account_data, ephemeral, ambiguity_changes }
223 }
224}
225
226#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
228pub struct UnreadNotificationsCount {
229 pub highlight_count: u64,
232 pub notification_count: u64,
234}
235
236impl From<RumaUnreadNotificationsCount> for UnreadNotificationsCount {
237 fn from(notifications: RumaUnreadNotificationsCount) -> Self {
238 Self {
239 highlight_count: notifications.highlight_count.map(|c| c.into()).unwrap_or(0),
240 notification_count: notifications.notification_count.map(|c| c.into()).unwrap_or(0),
241 }
242 }
243}
244
245#[derive(Clone, Default)]
247pub struct LeftRoomUpdate {
248 pub timeline: Timeline,
251 pub state: Vec<Raw<AnySyncStateEvent>>,
256 pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
258 pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
263}
264
265impl LeftRoomUpdate {
266 pub(crate) fn new(
267 timeline: Timeline,
268 state: Vec<Raw<AnySyncStateEvent>>,
269 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
270 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
271 ) -> Self {
272 Self { timeline, state, account_data, ambiguity_changes }
273 }
274}
275
276#[cfg(not(tarpaulin_include))]
277impl fmt::Debug for LeftRoomUpdate {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.debug_struct("LeftRoomUpdate")
280 .field("timeline", &self.timeline)
281 .field("state", &DebugListOfRawEvents(&self.state))
282 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
283 .field("ambiguity_changes", &self.ambiguity_changes)
284 .finish()
285 }
286}
287
288#[derive(Clone, Debug, Default)]
290pub struct Timeline {
291 pub limited: bool,
294
295 pub prev_batch: Option<String>,
298
299 pub events: Vec<TimelineEvent>,
301}
302
303impl Timeline {
304 pub(crate) fn new(limited: bool, prev_batch: Option<String>) -> Self {
305 Self { limited, prev_batch, ..Default::default() }
306 }
307}
308
309struct DebugInvitedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, InvitedRoomUpdate>);
310
311#[cfg(not(tarpaulin_include))]
312impl fmt::Debug for DebugInvitedRoomUpdates<'_> {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugInvitedRoom(v)))).finish()
315 }
316}
317
318struct DebugKnockedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, KnockedRoomUpdate>);
319
320#[cfg(not(tarpaulin_include))]
321impl fmt::Debug for DebugKnockedRoomUpdates<'_> {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugKnockedRoom(v)))).finish()
324 }
325}
326
327#[derive(Clone)]
329pub struct Notification {
330 pub actions: Vec<Action>,
332
333 pub event: RawAnySyncOrStrippedTimelineEvent,
335}
336
337#[cfg(not(tarpaulin_include))]
338impl fmt::Debug for Notification {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 let event_debug = match &self.event {
341 RawAnySyncOrStrippedTimelineEvent::Sync(ev) => DebugRawEvent(ev),
342 RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => DebugRawEvent(ev.cast_ref()),
343 };
344
345 f.debug_struct("Notification")
346 .field("actions", &self.actions)
347 .field("event", &event_debug)
348 .finish()
349 }
350}