doc: document the App API

This commit is contained in:
Benjamin Bouvier
2023-07-07 13:03:36 +02:00
parent 704cd7784b
commit e60d04b368

View File

@@ -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<RoomListService>,
encryption_sync: Option<Arc<EncryptionSync>>,
stream_task_handle: Arc<Mutex<Option<JoinHandle<()>>>>,
task_handle: Arc<Mutex<Option<JoinHandle<()>>>>,
state_observer: Observable<AppState>,
}
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<Item = AppState> {
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<String>,
) -> 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<App, Error> {
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),
}