matrix_sdk_base/
sync.rs

1// Copyright 2022 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The SDK's representation of the result of a `/sync` request.
16
17use std::{collections::BTreeMap, fmt};
18
19use matrix_sdk_common::{debug::DebugRawEvent, deserialized_responses::TimelineEvent};
20pub use ruma::api::client::sync::sync_events::v3::{
21    InvitedRoom as InvitedRoomUpdate, KnockedRoom as KnockedRoomUpdate,
22};
23use ruma::{
24    api::client::sync::sync_events::UnreadNotificationsCount as RumaUnreadNotificationsCount,
25    events::{
26        presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent,
27        AnySyncEphemeralRoomEvent, AnySyncStateEvent, AnyToDeviceEvent,
28    },
29    push::Action,
30    serde::Raw,
31    OwnedEventId, OwnedRoomId,
32};
33use serde::{Deserialize, Serialize};
34
35use crate::{
36    debug::{DebugInvitedRoom, DebugKnockedRoom, DebugListOfRawEvents, DebugListOfRawEventsNoId},
37    deserialized_responses::{AmbiguityChange, RawAnySyncOrStrippedTimelineEvent},
38};
39
40/// Generalized representation of a `/sync` response.
41///
42/// This type is intended to be applicable regardless of the endpoint used for
43/// syncing.
44#[derive(Clone, Default)]
45pub struct SyncResponse {
46    /// Updates to rooms.
47    pub rooms: RoomUpdates,
48    /// Updates to the presence status of other users.
49    pub presence: Vec<Raw<PresenceEvent>>,
50    /// The global private data created by this user.
51    pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>,
52    /// Messages sent directly between devices.
53    pub to_device: Vec<Raw<AnyToDeviceEvent>>,
54    /// New notifications per room.
55    pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>,
56}
57
58#[cfg(not(tarpaulin_include))]
59impl fmt::Debug for SyncResponse {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.debug_struct("SyncResponse")
62            .field("rooms", &self.rooms)
63            .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
64            .field("to_device", &DebugListOfRawEventsNoId(&self.to_device))
65            .field("notifications", &self.notifications)
66            .finish_non_exhaustive()
67    }
68}
69
70/// Updates to rooms in a [`SyncResponse`].
71#[derive(Clone, Default)]
72pub struct RoomUpdates {
73    /// The rooms that the user has left or been banned from.
74    pub left: BTreeMap<OwnedRoomId, LeftRoomUpdate>,
75    /// The rooms that the user has joined.
76    pub joined: BTreeMap<OwnedRoomId, JoinedRoomUpdate>,
77    /// The rooms that the user has been invited to.
78    pub invited: BTreeMap<OwnedRoomId, InvitedRoomUpdate>,
79    /// The rooms that the user has knocked on.
80    pub knocked: BTreeMap<OwnedRoomId, KnockedRoomUpdate>,
81}
82
83#[cfg(not(tarpaulin_include))]
84impl fmt::Debug for RoomUpdates {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        f.debug_struct("RoomUpdates")
87            .field("leave", &self.left)
88            .field("join", &self.joined)
89            .field("invite", &DebugInvitedRoomUpdates(&self.invited))
90            .field("knocked", &DebugKnockedRoomUpdates(&self.knocked))
91            .finish()
92    }
93}
94
95/// Updates to joined rooms.
96#[derive(Clone, Default)]
97pub struct JoinedRoomUpdate {
98    /// Counts of unread notifications for this room.
99    pub unread_notifications: UnreadNotificationsCount,
100    /// The timeline of messages and state changes in the room.
101    pub timeline: Timeline,
102    /// Updates to the state, between the time indicated by the `since`
103    /// parameter, and the start of the `timeline` (or all state up to the
104    /// start of the `timeline`, if `since` is not given, or `full_state` is
105    /// true).
106    pub state: Vec<Raw<AnySyncStateEvent>>,
107    /// The private data that this user has attached to this room.
108    pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
109    /// The ephemeral events in the room that aren't recorded in the timeline or
110    /// state of the room. e.g. typing.
111    pub ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
112    /// Collection of ambiguity changes that room member events trigger.
113    ///
114    /// This is a map of event ID of the `m.room.member` event to the
115    /// details of the ambiguity change.
116    pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
117}
118
119#[cfg(not(tarpaulin_include))]
120impl fmt::Debug for JoinedRoomUpdate {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_struct("JoinedRoomUpdate")
123            .field("unread_notifications", &self.unread_notifications)
124            .field("timeline", &self.timeline)
125            .field("state", &DebugListOfRawEvents(&self.state))
126            .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
127            .field("ephemeral", &self.ephemeral)
128            .field("ambiguity_changes", &self.ambiguity_changes)
129            .finish()
130    }
131}
132
133impl JoinedRoomUpdate {
134    pub(crate) fn new(
135        timeline: Timeline,
136        state: Vec<Raw<AnySyncStateEvent>>,
137        account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
138        ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
139        unread_notifications: UnreadNotificationsCount,
140        ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
141    ) -> Self {
142        Self { unread_notifications, timeline, state, account_data, ephemeral, ambiguity_changes }
143    }
144}
145
146/// Counts of unread notifications for a room.
147#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
148pub struct UnreadNotificationsCount {
149    /// The number of unread notifications for this room with the highlight flag
150    /// set.
151    pub highlight_count: u64,
152    /// The total number of unread notifications for this room.
153    pub notification_count: u64,
154}
155
156impl From<RumaUnreadNotificationsCount> for UnreadNotificationsCount {
157    fn from(notifications: RumaUnreadNotificationsCount) -> Self {
158        Self {
159            highlight_count: notifications.highlight_count.map(|c| c.into()).unwrap_or(0),
160            notification_count: notifications.notification_count.map(|c| c.into()).unwrap_or(0),
161        }
162    }
163}
164
165/// Updates to left rooms.
166#[derive(Clone, Default)]
167pub struct LeftRoomUpdate {
168    /// The timeline of messages and state changes in the room up to the point
169    /// when the user left.
170    pub timeline: Timeline,
171    /// Updates to the state, between the time indicated by the `since`
172    /// parameter, and the start of the `timeline` (or all state up to the
173    /// start of the `timeline`, if `since` is not given, or `full_state` is
174    /// true).
175    pub state: Vec<Raw<AnySyncStateEvent>>,
176    /// The private data that this user has attached to this room.
177    pub account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
178    /// Collection of ambiguity changes that room member events trigger.
179    ///
180    /// This is a map of event ID of the `m.room.member` event to the
181    /// details of the ambiguity change.
182    pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
183}
184
185impl LeftRoomUpdate {
186    pub(crate) fn new(
187        timeline: Timeline,
188        state: Vec<Raw<AnySyncStateEvent>>,
189        account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
190        ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
191    ) -> Self {
192        Self { timeline, state, account_data, ambiguity_changes }
193    }
194}
195
196#[cfg(not(tarpaulin_include))]
197impl fmt::Debug for LeftRoomUpdate {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        f.debug_struct("LeftRoomUpdate")
200            .field("timeline", &self.timeline)
201            .field("state", &DebugListOfRawEvents(&self.state))
202            .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
203            .field("ambiguity_changes", &self.ambiguity_changes)
204            .finish()
205    }
206}
207
208/// Events in the room.
209#[derive(Clone, Debug, Default)]
210pub struct Timeline {
211    /// True if the number of events returned was limited by the `limit` on the
212    /// filter.
213    pub limited: bool,
214
215    /// A token that can be supplied to to the `from` parameter of the
216    /// `/rooms/{roomId}/messages` endpoint.
217    pub prev_batch: Option<String>,
218
219    /// A list of events.
220    pub events: Vec<TimelineEvent>,
221}
222
223impl Timeline {
224    pub(crate) fn new(limited: bool, prev_batch: Option<String>) -> Self {
225        Self { limited, prev_batch, ..Default::default() }
226    }
227}
228
229struct DebugInvitedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, InvitedRoomUpdate>);
230
231#[cfg(not(tarpaulin_include))]
232impl fmt::Debug for DebugInvitedRoomUpdates<'_> {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugInvitedRoom(v)))).finish()
235    }
236}
237
238struct DebugKnockedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, KnockedRoomUpdate>);
239
240#[cfg(not(tarpaulin_include))]
241impl fmt::Debug for DebugKnockedRoomUpdates<'_> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugKnockedRoom(v)))).finish()
244    }
245}
246
247/// A notification triggered by a sync response.
248#[derive(Clone)]
249pub struct Notification {
250    /// The actions to perform when the conditions for this rule are met.
251    pub actions: Vec<Action>,
252
253    /// The event that triggered the notification.
254    pub event: RawAnySyncOrStrippedTimelineEvent,
255}
256
257#[cfg(not(tarpaulin_include))]
258impl fmt::Debug for Notification {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        let event_debug = match &self.event {
261            RawAnySyncOrStrippedTimelineEvent::Sync(ev) => DebugRawEvent(ev),
262            RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => DebugRawEvent(ev.cast_ref()),
263        };
264
265        f.debug_struct("Notification")
266            .field("actions", &self.actions)
267            .field("event", &event_debug)
268            .finish()
269    }
270}