From e60d04b368a5810799109daddd0875d024f103e8 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 7 Jul 2023 13:03:36 +0200 Subject: [PATCH] doc: document the App API --- crates/matrix-sdk-ui/src/app/mod.rs | 79 +++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/crates/matrix-sdk-ui/src/app/mod.rs b/crates/matrix-sdk-ui/src/app/mod.rs index f3a28037d..4f0e7ac40 100644 --- a/crates/matrix-sdk-ui/src/app/mod.rs +++ b/crates/matrix-sdk-ui/src/app/mod.rs @@ -30,39 +30,61 @@ use crate::{ room_list::{self, RoomListService}, }; +/// Current state of the application. +/// +/// This is a high-level state indicating what's the status of the underlying syncs. +/// The application starts in `Running` mode, and then hits a terminal state `Terminated` (if it +/// gracefully exited) or `Error` (in case any of the underlying syncs ran into an error). +/// +/// It is the responsibility of the caller to restart the application using the [`App::start`] +/// method, in case it terminated, gracefully or not. +/// +/// This can be observed with [`App::observe_state`]. #[derive(Clone)] pub enum AppState { + /// The underlying syncs are properly running in the background. Running, + /// Any of the underlying syncs has terminated gracefully (i.e. be stopped). Terminated, + /// Any of the underlying syncs has ran into an error. Error, } pub struct App { room_list: Arc, encryption_sync: Option>, - stream_task_handle: Arc>>>, + task_handle: Arc>>>, state_observer: Observable, } impl App { + /// Create a new builder for configuring an `App`. pub fn builder(client: Client) -> AppBuilder { AppBuilder::new(client) } + /// Get the underlying `RoomListService` instance for easier access to its methods. pub fn room_list_service(&self) -> &RoomListService { &*self.room_list } + /// Observe the current state of the application. + /// + /// See also [`AppState`]. pub fn observe_state(&self) -> impl Stream { self.state_observer.subscribe() } + /// Start (or restart) the underlying sliding syncs. + /// + /// This can be called multiple times safely: if a previous task had been spawned to run the + /// syncs, then it will be properly aborted and restarted. pub async fn start(&self) -> Result<(), Error> { let room_list = self.room_list.clone(); let encryption_sync = self.encryption_sync.clone(); let state_observer = self.state_observer.clone(); - let mut task_handle_lock = self.stream_task_handle.lock().unwrap(); + let mut task_handle_lock = self.task_handle.lock().unwrap(); // If there was a task running already with the streams, stop it gently. In the case it was // already terminated, that's fine as it won't cause any harm to abort it. @@ -129,6 +151,10 @@ impl App { Ok(()) } + /// Stop the underlying sliding syncs. + /// + /// This must be called when the app goes into the background. It's better to call this API + /// when the application exits, although not strictly necessary. pub fn pause(&self) -> Result<(), Error> { self.room_list.stop_sync()?; if let Some(ref encryption_sync) = self.encryption_sync { @@ -139,10 +165,18 @@ impl App { } pub struct AppBuilder { + /// SDK client. client: Client, - identifier: String, - with_cross_process_lock: bool, + + /// Is the encryption sync running as a separate instance of sliding sync (true), or is it + /// fused in the main `RoomList` sliding sync (false)? with_encryption_sync: bool, + + /// Is the cross-process lock for the crypto store enabled? + with_cross_process_lock: bool, + + /// Application identifier, used the cross-process lock value, if applicable. + identifier: String, } impl AppBuilder { @@ -155,17 +189,37 @@ impl AppBuilder { } } - pub fn identifier(mut self, name: String) -> Self { - self.identifier = name; - self - } - - pub fn with_encryption_sync(mut self, with_cross_process_lock: bool) -> Self { + /// Enables the encryption sync for this application. + /// + /// This will run a second sliding sync instance, that can independently process encryption + /// events, which can speed up some use cases. + /// + /// It's also a prerequisite if another process can *also* process encryption events; in that + /// case, the `with_cross_process_lock` boolean must be set to `true` to enable the + /// cross-process crypto store lock. This is only applicable to very specific use cases, like + /// an external process attempting to decrypt notifications. In general, + /// `with_cross_process_lock` can remain `false`. + /// + /// If the cross-process lock is enabled, then an app identifier can be provided too, to + /// identify the current process; if it's not provided, a default value of "app" is used as the + /// application identifier. + pub fn with_encryption_sync( + mut self, + with_cross_process_lock: bool, + app_identifier: Option, + ) -> Self { self.with_encryption_sync = true; self.with_cross_process_lock = with_cross_process_lock; + if let Some(app_identifier) = app_identifier { + self.identifier = app_identifier; + } self } + /// Finish setting up the `App`. + /// + /// This creates the underlying sliding syncs, and will start them in the background. The + /// resulting `App` must be kept alive as long as the sliding syncs are supposed to run. pub async fn build(self) -> Result { let (room_list, encryption_sync) = if self.with_encryption_sync { let room_list = RoomListService::new(self.client.clone()).await?; @@ -183,7 +237,7 @@ impl AppBuilder { room_list: Arc::new(room_list), encryption_sync, state_observer: Observable::new(AppState::Running), - stream_task_handle: Default::default(), + task_handle: Default::default(), }; app.start().await?; @@ -192,11 +246,14 @@ impl AppBuilder { } } +/// Errors for the `App` API. #[derive(Debug, Error)] pub enum Error { + /// An error received from the `RoomList` API. #[error(transparent)] RoomList(#[from] room_list::Error), + /// An error received from the `EncryptionSync` API. #[error(transparent)] EncryptionSync(#[from] encryption_sync::Error), }