1#![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#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
89pub struct SessionMeta {
90 pub user_id: OwnedUserId,
92 pub device_id: OwnedDeviceId,
94}
95
96pub struct Cancellable<'a, T> {
105 token: CancellationToken,
106 future: BoxFuture<'a, Option<T>>,
107}
108
109impl<T> Cancellable<'_, T> {
110 pub fn cancellation_token(&self) -> CancellationToken {
112 self.token.clone()
113 }
114
115 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
135pub trait CancellableIntoFutureExt: IntoFuture + Sized {
138 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 assert!(!polled.load(Ordering::SeqCst));
192 }
193
194 #[async_test]
195 async fn test_cancellable_resolves_to_none_when_cancelled_from_another_task() {
196 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}