Skip to main content

matrix_sdk_base/
lib.rs

1// Copyright 2020 Damir Jelić
2// Copyright 2020 The Matrix.org Foundation C.I.C.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![doc = include_str!("../README.md")]
17#![cfg_attr(docsrs, feature(doc_cfg))]
18#![cfg_attr(target_family = "wasm", allow(clippy::arc_with_non_send_sync))]
19#![warn(missing_docs, missing_debug_implementations)]
20
21use std::{
22    fmt,
23    pin::Pin,
24    task::{Context, Poll},
25};
26
27pub use matrix_sdk_common::*;
28use ruma::{OwnedDeviceId, OwnedUserId};
29use serde::{Deserialize, Serialize};
30use tokio_util::sync::CancellationToken;
31
32pub use crate::error::{Error, Result};
33
34mod client;
35pub use client::RequestedRequiredStates;
36pub mod debug;
37pub mod deserialized_responses;
38mod error;
39pub mod event_cache;
40pub mod latest_event;
41pub mod media;
42pub mod notification_settings;
43pub mod read_receipts;
44mod response_processors;
45mod room;
46
47pub mod sliding_sync;
48
49#[cfg(feature = "unstable-msc4354")]
50pub mod sticky;
51
52pub mod store;
53pub mod sync;
54#[cfg(any(test, feature = "testing"))]
55mod test_utils;
56mod utils;
57
58pub use client::DmRoomDefinition;
59
60#[cfg(feature = "experimental-element-recent-emojis")]
61pub mod recent_emojis;
62
63#[cfg(feature = "uniffi")]
64uniffi::setup_scaffolding!();
65
66pub use client::{BaseClient, ThreadingSupport};
67#[cfg(any(test, feature = "testing"))]
68pub use http;
69#[cfg(feature = "e2e-encryption")]
70pub use matrix_sdk_crypto as crypto;
71pub use room::{
72    CallIntentConsensus, EncryptionState, PredecessorRoom, Room, RoomCreateWithCreatorEventContent,
73    RoomDisplayName, RoomHero, RoomHeroWithProfile, RoomInfo, RoomInfoNotableUpdate,
74    RoomInfoNotableUpdateReasons, RoomMember, RoomMembersUpdate, RoomMemberships, RoomRecencyStamp,
75    RoomState, RoomStateFilter, SuccessorRoom, apply_redaction,
76};
77pub use store::{
78    ComposerDraft, ComposerDraftType, DraftAttachment, DraftAttachmentContent, DraftThumbnail,
79    QueueWedgeError, StateChanges, StateStore, StateStoreDataKey, StateStoreDataValue, StoreError,
80    ThreadSubscriptionCatchupToken,
81};
82pub use utils::{MinimalRoomMemberEvent, MinimalStateEvent, RawStateEventWithKeys};
83
84#[cfg(test)]
85matrix_sdk_test_utils::init_tracing_for_tests!();
86
87/// The Matrix user session info.
88#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
89pub struct SessionMeta {
90    /// The ID of the session's user.
91    pub user_id: OwnedUserId,
92    /// The ID of the client device.
93    pub device_id: OwnedDeviceId,
94}
95
96/// A future that can be cancelled cooperatively, created with
97/// [`CancellableIntoFutureExt::cancellable`].
98///
99/// Resolves to `Some` with the output of the underlying future if it completes
100/// first, or to `None` if it is cancelled first, either with [`Self::cancel`]
101/// or through the [`CancellationToken`] returned by
102/// [`Self::cancellation_token`]. The latter can be handed to another task to
103/// cancel the future while it is being awaited.
104pub struct Cancellable<'a, T> {
105    token: CancellationToken,
106    future: BoxFuture<'a, Option<T>>,
107}
108
109impl<T> Cancellable<'_, T> {
110    /// Get a [`CancellationToken`] to cancel this future from elsewhere.
111    pub fn cancellation_token(&self) -> CancellationToken {
112        self.token.clone()
113    }
114
115    /// Cancel this future, making it resolve to `None`.
116    pub fn cancel(&self) {
117        self.token.cancel();
118    }
119}
120
121impl<T> Future for Cancellable<'_, T> {
122    type Output = Option<T>;
123
124    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
125        self.future.as_mut().poll(cx)
126    }
127}
128
129impl<T> fmt::Debug for Cancellable<'_, T> {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.debug_struct("Cancellable").field("token", &self.token).finish_non_exhaustive()
132    }
133}
134
135/// Extension trait for wrapping an [`IntoFuture`] with cooperative
136/// cancellation.
137pub trait CancellableIntoFutureExt: IntoFuture + Sized {
138    /// Wrap this future in a [`Cancellable`], which resolves to `Some` with the
139    /// output of this future if it completes first, or to `None` if it is
140    /// cancelled first.
141    fn cancellable<'a>(self) -> Cancellable<'a, Self::Output>
142    where
143        Self::IntoFuture: SendOutsideWasm + 'a,
144    {
145        let token = CancellationToken::new();
146        let future = Box::pin(token.clone().run_until_cancelled_owned(self.into_future()));
147
148        Cancellable { token, future }
149    }
150}
151
152impl<T: IntoFuture> CancellableIntoFutureExt for T {}
153
154#[cfg(test)]
155mod tests {
156    use std::{
157        future::pending,
158        sync::{
159            Arc,
160            atomic::{AtomicBool, Ordering},
161        },
162    };
163
164    use matrix_sdk_common::executor::spawn;
165    use matrix_sdk_test::async_test;
166
167    use super::CancellableIntoFutureExt;
168
169    #[async_test]
170    async fn test_cancellable_resolves_to_some_when_not_cancelled() {
171        assert_eq!(async { 42 }.cancellable().await, Some(42));
172    }
173
174    #[async_test]
175    async fn test_cancellable_resolves_to_none_when_cancelled_before_being_polled() {
176        let polled = Arc::new(AtomicBool::new(false));
177        let future = {
178            let polled = polled.clone();
179            async move {
180                polled.store(true, Ordering::SeqCst);
181                42
182            }
183        };
184
185        let cancellable = future.cancellable();
186        cancellable.cancel();
187
188        assert_eq!(cancellable.await, None);
189        // The wrapped future isn't even polled once when the cancellation
190        // happened before it was first awaited.
191        assert!(!polled.load(Ordering::SeqCst));
192    }
193
194    #[async_test]
195    async fn test_cancellable_resolves_to_none_when_cancelled_from_another_task() {
196        // Use a future that never completes so that only the cancellation can
197        // make the `Cancellable` resolve.
198        let cancellable = pending::<()>().cancellable();
199        let token = cancellable.cancellation_token();
200
201        spawn(async move { token.cancel() });
202
203        assert_eq!(cancellable.await, None);
204    }
205}