From 1ea139870ecc4a6201646a1662becbac2378cd16 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Tue, 10 Jun 2025 15:27:38 -0400 Subject: [PATCH 01/18] Change matrix-sdk-ffi to rely on features over platform targets The system of platform targets was already quite messy, and becoming even worse as we start preparing for Wasm support. Switch to features instead to make this easier to work with. --- Cargo.lock | 2 +- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4733bb4f7e0..271dd24ab7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3089,7 +3089,7 @@ dependencies = [ [[package]] name = "matrix-sdk-ffi" -version = "0.12.0" +version = "0.11.0" dependencies = [ "anyhow", "as_variant", diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 117a1204b00..a4a13954888 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "matrix-sdk-ffi" -version = "0.12.0" +version = "0.11.0" edition = "2021" homepage = "https://github.com/matrix-org/matrix-rust-sdk" keywords = ["matrix", "chat", "messaging", "ffi"] From bd2de9c09fb46a09dee11d886c995dbbaea1006d Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Wed, 18 Jun 2025 09:28:39 -0400 Subject: [PATCH 02/18] Set up feature flags for new indexeddb and sqlite choices --- bindings/matrix-sdk-ffi/Cargo.toml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index a4a13954888..50f7b0fe0a6 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -25,17 +25,24 @@ crate-type = [ [features] default = ["bundled-sqlite", "unstable-msc4274"] -bundled-sqlite = ["matrix-sdk/bundled-sqlite"] +bundled-sqlite = ["sqlite", "matrix-sdk/bundled-sqlite"] unstable-msc4274 = ["matrix-sdk-ui/unstable-msc4274"] +# Use indexeddb for session storage, supported only on Wasm platforms. +indexeddb = ["matrix-sdk/indexeddb"] +# Use sqlite for session storage, not supported on Wasm platforms. +sqlite = ["matrix-sdk/sqlite"] +# provide a unified API regardless of target platforms, for use with multi-target platforms like react-native +react-native = [] # Required when targeting a Javascript environment, like Wasm in a browser. -js = ["matrix-sdk-ui/js"] +js = ["matrix-sdk-ui/js", "uuid/js"] # Use the TLS implementation provided by the host system, necessary on iOS and Wasm platforms. -native-tls = ["matrix-sdk/native-tls", "sentry?/native-tls"] +native-tls = ["matrix-sdk/native-tls", "sentry?/native-tls", "graphql_client/reqwest"] # Use Rustls as the TLS implementation, necessary on Android platforms. -rustls-tls = ["matrix-sdk/rustls-tls", "sentry?/rustls"] -# Enable sentry error monitoring, not compatible with Wasm platforms. +rustls-tls = ["matrix-sdk/rustls-tls", "sentry?/rustls", "graphql_client/reqwest-rustls"] +# Enable sentry error monitoring, not compatible on Wasm platforms. sentry = ["dep:sentry", "dep:sentry-tracing"] + [dependencies] anyhow.workspace = true as_variant.workspace = true @@ -50,7 +57,6 @@ matrix-sdk = { workspace = true, features = [ "experimental-widgets", "markdown", "socks", - "sqlite", "uniffi", ] } matrix-sdk-common.workspace = true From 6c5cfeb2548bd132b4d3e9997557ead7df40d748 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Wed, 18 Jun 2025 10:49:13 -0400 Subject: [PATCH 03/18] Add support for indexeddb and sqlite session choice --- bindings/matrix-sdk-ffi/CHANGELOG.md | 23 ++ bindings/matrix-sdk-ffi/Cargo.toml | 6 +- bindings/matrix-sdk-ffi/README.md | 4 +- bindings/matrix-sdk-ffi/build.rs | 26 +- bindings/matrix-sdk-ffi/src/client_builder.rs | 176 ++++---------- bindings/matrix-sdk-ffi/src/lib.rs | 1 + bindings/matrix-sdk-ffi/src/session_store.rs | 222 ++++++++++++++++++ 7 files changed, 322 insertions(+), 136 deletions(-) create mode 100644 bindings/matrix-sdk-ffi/src/session_store.rs diff --git a/bindings/matrix-sdk-ffi/CHANGELOG.md b/bindings/matrix-sdk-ffi/CHANGELOG.md index 556cd0b15ca..2517a43e2e9 100644 --- a/bindings/matrix-sdk-ffi/CHANGELOG.md +++ b/bindings/matrix-sdk-ffi/CHANGELOG.md @@ -6,6 +6,29 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - ReleaseDate +### Features +- Configuration of the session store has been changed to allow for use of either indexeddb +or sqlite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. + +Previously the code to configure a sqlite session would look something like this in a host language: +``` +builder + .sessionPaths("data_path", "cache_path") + .passphrase("foobar") +``` +With the new system, a helper object is exposed for either Sqlite or IndexedDB to group those settings. +``` +builder + .session_store_sqlite( + SqliteSessionStoreBuilder.new({ dataPath: "data_path", cachePath: "cache_path" }) + .passphrase("foobar") + ) +``` + +The following methods from `ClientBuilder` have been moved onto `SqliteSessionStoreBuilder`: +`session_paths`, `session_passphrase`, `session_pool_max_size`, `session_cache_size`, and `session_journal_size_limit`. + + ### Refactor - Adjust features in the `matrix-sdk-ffi` crate to expose more platform-specific knobs. diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 50f7b0fe0a6..05ee207df5c 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -31,14 +31,12 @@ unstable-msc4274 = ["matrix-sdk-ui/unstable-msc4274"] indexeddb = ["matrix-sdk/indexeddb"] # Use sqlite for session storage, not supported on Wasm platforms. sqlite = ["matrix-sdk/sqlite"] -# provide a unified API regardless of target platforms, for use with multi-target platforms like react-native -react-native = [] # Required when targeting a Javascript environment, like Wasm in a browser. js = ["matrix-sdk-ui/js", "uuid/js"] # Use the TLS implementation provided by the host system, necessary on iOS and Wasm platforms. -native-tls = ["matrix-sdk/native-tls", "sentry?/native-tls", "graphql_client/reqwest"] +native-tls = ["matrix-sdk/native-tls", "sentry?/native-tls"] # Use Rustls as the TLS implementation, necessary on Android platforms. -rustls-tls = ["matrix-sdk/rustls-tls", "sentry?/rustls", "graphql_client/reqwest-rustls"] +rustls-tls = ["matrix-sdk/rustls-tls", "sentry?/rustls"] # Enable sentry error monitoring, not compatible on Wasm platforms. sentry = ["dep:sentry", "dep:sentry-tracing"] diff --git a/bindings/matrix-sdk-ffi/README.md b/bindings/matrix-sdk-ffi/README.md index a514194c818..114d4055d06 100644 --- a/bindings/matrix-sdk-ffi/README.md +++ b/bindings/matrix-sdk-ffi/README.md @@ -12,6 +12,8 @@ Given the number of platforms targeted, we have broken out a number of features ### Functionality - `sentry`: Enable error monitoring using Sentry, not supports on Wasm platforms. - `bundled-sqlite`: Use an embedded version of sqlite instead of the system provided one. +- `sqlite`: Use sqlite for session storage, not available on Wasm platforms. +- `indexeddb`: Use IndexedDb for session storage, only available on Wasm platforms. ### Unstable specs - `unstable-msc4274`: Adds support for gallery message types, which contain multiple media elements. @@ -22,7 +24,7 @@ Each supported target should use features to select the relevant TLS system. He - Android: `"bundled-sqlite,unstable-msc4274,rustls-tls,sentry"` - iOS: `"bundled-sqlite,unstable-msc4274,native-tls,sentry"` -- Javascript/Wasm: `"unstable-msc4274,native-tls"` +- Javascript/Wasm: `"indexeddb,unstable-msc4274,native-tls"` ### Swift/iOS sync diff --git a/bindings/matrix-sdk-ffi/build.rs b/bindings/matrix-sdk-ffi/build.rs index d58dc7a1690..c28765bf36d 100644 --- a/bindings/matrix-sdk-ffi/build.rs +++ b/bindings/matrix-sdk-ffi/build.rs @@ -2,7 +2,7 @@ use std::{ env, error::Error, path::{Path, PathBuf}, - process::Command, + process::{self, Command}, }; use vergen::EmitBuilder; @@ -56,7 +56,31 @@ fn get_clang_major_version(clang_path: &Path) -> String { clang_version.split('.').next().expect("could not parse clang output").to_owned() } +fn env_is_set(var_name: &str) -> bool { + env::var_os(var_name).is_some() +} + +fn ensure(cond: bool, err: &str) { + if !cond { + eprintln!( + "\n\ + ┏━━━━━━━━{pad}━┓\n\ + ┃ error: {err} ┃\n\ + ┗━━━━━━━━{pad}━┛\n\ + ", + pad = "━".repeat(err.len()), + ); + process::exit(1); + } +} + fn main() -> Result<(), Box> { + let sqlite_set = env_is_set("CARGO_FEATURE_SQLITE"); + let indexeddb_set = env_is_set("CARGO_FEATURE_INDEXEDDB"); + ensure( + sqlite_set || indexeddb_set, + "one of the features 'sqlite' or 'indexeddb' must be enabled", + ); setup_x86_64_android_workaround(); uniffi::generate_scaffolding("./src/api.udl").expect("Building the UDL file failed"); EmitBuilder::builder().git_sha(true).emit()?; diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index c76fc498298..8c7a569d550 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -1,4 +1,4 @@ -use std::{fs, num::NonZeroUsize, path::Path, sync::Arc, time::Duration}; +use std::{num::NonZeroUsize, sync::Arc, time::Duration}; use futures_util::StreamExt; use matrix_sdk::{ @@ -12,11 +12,10 @@ use matrix_sdk::{ VersionBuilderError, }, Client as MatrixClient, ClientBuildError as MatrixClientBuildError, HttpError, IdParseError, - RumaApiError, SqliteStoreConfig, + RumaApiError, }; use ruma::api::error::{DeserializationError, FromHttpResponseError}; use tracing::{debug, error}; -use zeroize::Zeroizing; use super::client::Client; use crate::{ @@ -26,6 +25,7 @@ use crate::{ helpers::unwrap_or_clone_arc, qr_code::{HumanQrLoginError, QrCodeData, QrLoginProgressListener}, runtime::get_runtime_handle, + session_store::{SessionStoreConfig, SessionStoreResult}, task_handle::TaskHandle, }; @@ -108,11 +108,7 @@ impl From for ClientBuildError { #[derive(Clone, uniffi::Object)] pub struct ClientBuilder { - session_paths: Option, - session_passphrase: Zeroizing>, - session_pool_max_size: Option, - session_cache_size: Option, - session_journal_size_limit: Option, + session_store: Option, system_is_memory_constrained: bool, username: Option, homeserver_cfg: Option, @@ -138,11 +134,7 @@ impl ClientBuilder { #[uniffi::constructor] pub fn new() -> Arc { Arc::new(Self { - session_paths: None, - session_passphrase: Zeroizing::new(None), - session_pool_max_size: None, - session_cache_size: None, - session_journal_size_limit: None, + session_store: None, system_is_memory_constrained: false, username: None, homeserver_cfg: None, @@ -193,73 +185,6 @@ impl ClientBuilder { Arc::new(builder) } - /// Sets the paths that the client will use to store its data and caches. - /// Both paths **must** be unique per session as the SDK stores aren't - /// capable of handling multiple users, however it is valid to use the - /// same path for both stores on a single session. - /// - /// Leaving this unset tells the client to use an in-memory data store. - pub fn session_paths(self: Arc, data_path: String, cache_path: String) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.session_paths = Some(SessionPaths { data_path, cache_path }); - Arc::new(builder) - } - - /// Set the passphrase for the stores given to - /// [`ClientBuilder::session_paths`]. - pub fn session_passphrase(self: Arc, passphrase: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.session_passphrase = Zeroizing::new(passphrase); - Arc::new(builder) - } - - /// Set the pool max size for the SQLite stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store exposes an async pool of connections. This method controls - /// the size of the pool. The larger the pool is, the more memory is - /// consumed, but also the more the app is reactive because it doesn't need - /// to wait on a pool to be available to run queries. - /// - /// See [`SqliteStoreConfig::pool_max_size`] to learn more. - pub fn session_pool_max_size(self: Arc, pool_max_size: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.session_pool_max_size = pool_max_size - .map(|size| size.try_into().expect("`pool_max_size` is too large to fit in `usize`")); - Arc::new(builder) - } - - /// Set the cache size for the SQLite stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store exposes a SQLite connection. This method controls the cache - /// size, in **bytes (!)**. - /// - /// The cache represents data SQLite holds in memory at once per open - /// database file. The default cache implementation does not allocate the - /// full amount of cache memory all at once. Cache memory is allocated - /// in smaller chunks on an as-needed basis. - /// - /// See [`SqliteStoreConfig::cache_size`] to learn more. - pub fn session_cache_size(self: Arc, cache_size: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.session_cache_size = cache_size; - Arc::new(builder) - } - - /// Set the size limit for the SQLite WAL files of stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store uses the WAL journal mode. This method controls the size - /// limit of the WAL files, in **bytes (!)**. - /// - /// See [`SqliteStoreConfig::journal_size_limit`] to learn more. - pub fn session_journal_size_limit(self: Arc, limit: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.session_journal_size_limit = limit; - Arc::new(builder) - } - /// Tell the client that the system is memory constrained, like in a push /// notification process for example. /// @@ -425,50 +350,23 @@ impl ClientBuilder { inner_builder.cross_process_store_locks_holder_name(holder_name.clone()); } - let store_path = if let Some(session_paths) = &builder.session_paths { - // This is the path where both the state store and the crypto store will live. - let data_path = Path::new(&session_paths.data_path); - // This is the path where the event cache store will live. - let cache_path = Path::new(&session_paths.cache_path); - - debug!( - data_path = %data_path.to_string_lossy(), - event_cache_path = %cache_path.to_string_lossy(), - "Creating directories for data (state and crypto) and cache stores.", - ); - - fs::create_dir_all(data_path)?; - fs::create_dir_all(cache_path)?; - - let mut sqlite_store_config = if builder.system_is_memory_constrained { - SqliteStoreConfig::with_low_memory_config(data_path) - } else { - SqliteStoreConfig::new(data_path) - }; - - sqlite_store_config = - sqlite_store_config.passphrase(builder.session_passphrase.as_deref()); - - if let Some(size) = builder.session_pool_max_size { - sqlite_store_config = sqlite_store_config.pool_max_size(size); - } - - if let Some(size) = builder.session_cache_size { - sqlite_store_config = sqlite_store_config.cache_size(size); - } - - if let Some(limit) = builder.session_journal_size_limit { - sqlite_store_config = sqlite_store_config.journal_size_limit(limit); + let mut store_path = None; + if let Some(session_store) = builder.session_store { + match session_store.build()? { + #[cfg(feature = "indexeddb")] + SessionStoreResult::IndexedDb { name, passphrase } => { + inner_builder = inner_builder.indexeddb_store(&name, passphrase.as_deref()); + } + #[cfg(feature = "sqlite")] + SessionStoreResult::Sqlite { config, cache_path, store_path: data_path } => { + inner_builder = inner_builder + .sqlite_store_with_config_and_cache_path(config, Some(cache_path)); + store_path = Some(data_path); + } } - - inner_builder = inner_builder - .sqlite_store_with_config_and_cache_path(sqlite_store_config, Some(cache_path)); - - Some(data_path.to_owned()) } else { - debug!("Not using a store path."); - None - }; + debug!("Not using a session store.") + } // Determine server either from URL, server name or user ID. inner_builder = match builder.homeserver_cfg { @@ -646,14 +544,32 @@ impl ClientBuilder { } } -/// The store paths the client will use when built. -#[derive(Clone)] -struct SessionPaths { - /// The path that the client will use to store its data. - data_path: String, - /// The path that the client will use to store its caches. This path can be - /// the same as the data path if you prefer to keep everything in one place. - cache_path: String, +#[cfg(feature = "sqlite")] +#[matrix_sdk_ffi_macros::export] +impl ClientBuilder { + /// Tell the client to use sqlite to store session data. + pub fn session_store_sqlite( + self: Arc, + config: Arc, + ) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.session_store = Some(SessionStoreConfig::Sqlite(config.as_ref().clone())); + Arc::new(builder) + } +} + +#[cfg(feature = "indexeddb")] +#[matrix_sdk_ffi_macros::export] +impl ClientBuilder { + /// Tell the client to use IndexedDb to store session data. + pub fn session_store_indexeddb( + self: Arc, + config: Arc, + ) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.session_store = Some(SessionStoreConfig::IndexedDb(config.as_ref().clone())); + Arc::new(builder) + } } #[derive(Clone, uniffi::Record)] diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index cf64c47b93e..22b947acd10 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -28,6 +28,7 @@ mod room_member; mod room_preview; mod ruma; mod runtime; +mod session_store; mod session_verification; mod sync_service; mod task_handle; diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs new file mode 100644 index 00000000000..36c83f45a7a --- /dev/null +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -0,0 +1,222 @@ +use std::sync::Arc; +#[cfg(feature = "sqlite")] +use std::{ + fs, + path::{Path, PathBuf}, +}; + +#[cfg(feature = "sqlite")] +use matrix_sdk::SqliteStoreConfig; +use tracing::debug; +use zeroize::Zeroizing; + +use crate::{client_builder::ClientBuildError, helpers::unwrap_or_clone_arc}; + +/// The store paths the client will use when built. +#[derive(Clone)] +struct SessionPaths { + /// The path that the client will use to store its data. + data_path: String, + /// The path that the client will use to store its caches. This path can be + /// the same as the data path if you prefer to keep everything in one place. + cache_path: String, +} + +/// A builder for configuring a Sqlite session store. +#[cfg(feature = "sqlite")] +#[derive(Clone, uniffi::Object)] +pub struct SqliteSessionStoreBuilder { + paths: SessionPaths, + passphrase: Zeroizing>, + pool_max_size: Option, + cache_size: Option, + journal_size_limit: Option, + system_is_memory_constrained: bool, +} + +#[cfg(feature = "sqlite")] +#[matrix_sdk_ffi_macros::export] +impl SqliteSessionStoreBuilder { + /// Construct a SqliteSessionStoreBuilder and set the paths that the client + /// will use to store its data and caches. + /// + /// Both paths **must** be unique per session as the SDK stores aren't + /// capable of handling multiple users, however it is valid to use the + /// same path for both stores on a single session. + #[uniffi::constructor] + pub fn new(data_path: String, cache_path: String) -> Self { + Self { + paths: SessionPaths { data_path, cache_path }, + passphrase: Zeroizing::new(None), + pool_max_size: None, + cache_size: None, + journal_size_limit: None, + system_is_memory_constrained: false, + } + } + + /// Set the passphrase for the stores given to + /// [`ClientBuilder::paths`]. + pub fn passphrase(self: Arc, passphrase: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.passphrase = Zeroizing::new(passphrase); + Arc::new(builder) + } + + /// Set the pool max size for the SQLite stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store exposes an async pool of connections. This method controls + /// the size of the pool. The larger the pool is, the more memory is + /// consumed, but also the more the app is reactive because it doesn't need + /// to wait on a pool to be available to run queries. + /// + /// See [`SqliteStoreConfig::pool_max_size`] to learn more. + pub fn pool_max_size(self: Arc, pool_max_size: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.pool_max_size = pool_max_size + .map(|size| size.try_into().expect("`pool_max_size` is too large to fit in `usize`")); + Arc::new(builder) + } + + /// Set the cache size for the SQLite stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store exposes a SQLite connection. This method controls the cache + /// size, in **bytes (!)**. + /// + /// The cache represents data SQLite holds in memory at once per open + /// database file. The default cache implementation does not allocate the + /// full amount of cache memory all at once. Cache memory is allocated + /// in smaller chunks on an as-needed basis. + /// + /// See [`SqliteStoreConfig::cache_size`] to learn more. + pub fn cache_size(self: Arc, cache_size: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.cache_size = cache_size; + Arc::new(builder) + } + + /// Set the size limit for the SQLite WAL files of stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store uses the WAL journal mode. This method controls the size + /// limit of the WAL files, in **bytes (!)**. + /// + /// See [`SqliteStoreConfig::journal_size_limit`] to learn more. + pub fn journal_size_limit(self: Arc, limit: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.journal_size_limit = limit; + Arc::new(builder) + } + + /// Tell the client that the system is memory constrained, like in a push + /// notification process for example. + /// + /// So far, at the time of writing (2025-04-07), it changes the defaults of + /// [`SqliteStoreConfig`], so one might not need to call + /// [`ClientBuilder::session_cache_size`] and siblings for example. Please + /// check [`SqliteStoreConfig::with_low_memory_config`]. + pub fn system_is_memory_constrained(self: Arc) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.system_is_memory_constrained = true; + Arc::new(builder) + } +} + +#[cfg(feature = "indexeddb")] +#[derive(Clone, uniffi::Object)] +pub struct IndexedDbSessionStoreBuilder { + name: String, + passphrase: Option, +} + +#[cfg(feature = "indexeddb")] +#[matrix_sdk_ffi_macros::export] +impl IndexedDbSessionStoreBuilder { + #[uniffi::constructor] + pub fn new(name: String) -> Arc { + Arc::new(Self { name, passphrase: None }) + } + + /// Set the passphrase for the IndexedDB store. + pub fn passphrase(self: Arc, passphrase: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.passphrase = passphrase; + Arc::new(builder) + } +} + +/// Internal struct for tracking the choice of session store config. +#[derive(Clone)] +pub enum SessionStoreConfig { + #[cfg(feature = "sqlite")] + /// Setup the client to use the SQLite store. + Sqlite(SqliteSessionStoreBuilder), + + #[cfg(feature = "indexeddb")] + /// Setup the client to use the IndexedDB store. + IndexedDb(IndexedDbSessionStoreBuilder), +} + +/// The result of building a [`SessionStoreConfig`], with data that +/// can be passed directly to a ClientBuilder. +pub enum SessionStoreResult { + #[cfg(feature = "sqlite")] + Sqlite { config: SqliteStoreConfig, cache_path: PathBuf, store_path: PathBuf }, + #[cfg(feature = "indexeddb")] + IndexedDb { name: String, passphrase: Option }, +} + +impl SessionStoreConfig { + pub(crate) fn build(&self) -> Result { + match self { + #[cfg(feature = "sqlite")] + SessionStoreConfig::Sqlite(config) => { + let data_path = Path::new(&config.paths.data_path); + let cache_path = Path::new(&config.paths.cache_path); + + debug!( + data_path = %data_path.to_string_lossy(), + cache_path = %cache_path.to_string_lossy(), + "Creating directories for data and cache stores.", + ); + + fs::create_dir_all(data_path)?; + fs::create_dir_all(cache_path)?; + + let mut sqlite_store_config = if config.system_is_memory_constrained { + SqliteStoreConfig::with_low_memory_config(data_path) + } else { + SqliteStoreConfig::new(data_path) + }; + + sqlite_store_config = sqlite_store_config.passphrase(config.passphrase.as_deref()); + + if let Some(size) = config.pool_max_size { + sqlite_store_config = sqlite_store_config.pool_max_size(size); + } + + if let Some(size) = config.cache_size { + sqlite_store_config = sqlite_store_config.cache_size(size); + } + + if let Some(limit) = config.journal_size_limit { + sqlite_store_config = sqlite_store_config.journal_size_limit(limit); + } + + Ok(SessionStoreResult::Sqlite { + config: sqlite_store_config, + store_path: data_path.to_owned(), + cache_path: cache_path.to_owned(), + }) + } + + #[cfg(feature = "indexeddb")] + SessionStoreConfig::IndexedDb(config) => Ok(SessionStoreResult::IndexedDb { + name: config.name.clone(), + passphrase: config.passphrase.clone(), + }), + } + } +} From a148409a02d02b7bcbf1d562de609c087f4389af Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Thu, 19 Jun 2025 18:18:46 -0400 Subject: [PATCH 04/18] fix capitlizations --- bindings/matrix-sdk-ffi/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/matrix-sdk-ffi/CHANGELOG.md b/bindings/matrix-sdk-ffi/CHANGELOG.md index 2517a43e2e9..896daa451e1 100644 --- a/bindings/matrix-sdk-ffi/CHANGELOG.md +++ b/bindings/matrix-sdk-ffi/CHANGELOG.md @@ -7,8 +7,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - ReleaseDate ### Features -- Configuration of the session store has been changed to allow for use of either indexeddb -or sqlite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. +- Configuration of the session store has been changed to allow for use of either IndexedDb +or SQLite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. Previously the code to configure a sqlite session would look something like this in a host language: ``` @@ -16,7 +16,7 @@ builder .sessionPaths("data_path", "cache_path") .passphrase("foobar") ``` -With the new system, a helper object is exposed for either Sqlite or IndexedDB to group those settings. +With the new system, a helper object is exposed for either SQLite or IndexedDB to group those settings. ``` builder .session_store_sqlite( From 629b6109f4d32b8851051b79e447f985a1d4e501 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Thu, 19 Jun 2025 18:20:18 -0400 Subject: [PATCH 05/18] fix some bad merges --- Cargo.lock | 2 +- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 271dd24ab7b..4733bb4f7e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3089,7 +3089,7 @@ dependencies = [ [[package]] name = "matrix-sdk-ffi" -version = "0.11.0" +version = "0.12.0" dependencies = [ "anyhow", "as_variant", diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 05ee207df5c..e296bd18efc 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "matrix-sdk-ffi" -version = "0.11.0" +version = "0.12.0" edition = "2021" homepage = "https://github.com/matrix-org/matrix-rust-sdk" keywords = ["matrix", "chat", "messaging", "ffi"] From 5dc20db78e58189380b2cbe8a5f458e202f946d1 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Fri, 20 Jun 2025 08:07:21 -0400 Subject: [PATCH 06/18] Add sqlite to the ci task, refactor code to make cfg clearer --- bindings/matrix-sdk-ffi/src/session_store.rs | 372 ++++++++++--------- xtask/src/ci.rs | 2 +- xtask/src/fixup.rs | 2 +- 3 files changed, 201 insertions(+), 175 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index 36c83f45a7a..11e79182553 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -1,152 +1,227 @@ -use std::sync::Arc; #[cfg(feature = "sqlite")] -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::path::PathBuf; #[cfg(feature = "sqlite")] use matrix_sdk::SqliteStoreConfig; -use tracing::debug; -use zeroize::Zeroizing; -use crate::{client_builder::ClientBuildError, helpers::unwrap_or_clone_arc}; -/// The store paths the client will use when built. -#[derive(Clone)] -struct SessionPaths { - /// The path that the client will use to store its data. - data_path: String, - /// The path that the client will use to store its caches. This path can be - /// the same as the data path if you prefer to keep everything in one place. - cache_path: String, -} - -/// A builder for configuring a Sqlite session store. -#[cfg(feature = "sqlite")] -#[derive(Clone, uniffi::Object)] -pub struct SqliteSessionStoreBuilder { - paths: SessionPaths, - passphrase: Zeroizing>, - pool_max_size: Option, - cache_size: Option, - journal_size_limit: Option, - system_is_memory_constrained: bool, +/// The result of building a [`SessionStoreConfig`], with data that +/// can be passed directly to a ClientBuilder. +pub enum SessionStoreResult { + #[cfg(feature = "sqlite")] + Sqlite { config: SqliteStoreConfig, cache_path: PathBuf, store_path: PathBuf }, + #[cfg(feature = "indexeddb")] + IndexedDb { name: String, passphrase: Option }, } #[cfg(feature = "sqlite")] -#[matrix_sdk_ffi_macros::export] -impl SqliteSessionStoreBuilder { - /// Construct a SqliteSessionStoreBuilder and set the paths that the client - /// will use to store its data and caches. - /// - /// Both paths **must** be unique per session as the SDK stores aren't - /// capable of handling multiple users, however it is valid to use the - /// same path for both stores on a single session. - #[uniffi::constructor] - pub fn new(data_path: String, cache_path: String) -> Self { - Self { - paths: SessionPaths { data_path, cache_path }, - passphrase: Zeroizing::new(None), - pool_max_size: None, - cache_size: None, - journal_size_limit: None, - system_is_memory_constrained: false, - } +mod sqlite_session_store { + use std::{fs, path::Path, sync::Arc}; + + use matrix_sdk::SqliteStoreConfig; + use tracing::debug; + use zeroize::Zeroizing; + + use crate::helpers::unwrap_or_clone_arc; + use super::SessionStoreResult; + use crate::client_builder::ClientBuildError; + + /// The store paths the client will use when built. + #[derive(Clone)] + struct SessionPaths { + /// The path that the client will use to store its data. + data_path: String, + /// The path that the client will use to store its caches. This path can + /// be the same as the data path if you prefer to keep + /// everything in one place. + cache_path: String, } - /// Set the passphrase for the stores given to - /// [`ClientBuilder::paths`]. - pub fn passphrase(self: Arc, passphrase: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.passphrase = Zeroizing::new(passphrase); - Arc::new(builder) + /// A builder for configuring a Sqlite session store. + #[derive(Clone, uniffi::Object)] + pub struct SqliteSessionStoreBuilder { + paths: SessionPaths, + passphrase: Zeroizing>, + pool_max_size: Option, + cache_size: Option, + journal_size_limit: Option, + system_is_memory_constrained: bool, } - /// Set the pool max size for the SQLite stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store exposes an async pool of connections. This method controls - /// the size of the pool. The larger the pool is, the more memory is - /// consumed, but also the more the app is reactive because it doesn't need - /// to wait on a pool to be available to run queries. - /// - /// See [`SqliteStoreConfig::pool_max_size`] to learn more. - pub fn pool_max_size(self: Arc, pool_max_size: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.pool_max_size = pool_max_size - .map(|size| size.try_into().expect("`pool_max_size` is too large to fit in `usize`")); - Arc::new(builder) - } + #[matrix_sdk_ffi_macros::export] + impl SqliteSessionStoreBuilder { + /// Construct a SqliteSessionStoreBuilder and set the paths that the + /// client will use to store its data and caches. + /// + /// Both paths **must** be unique per session as the SDK stores aren't + /// capable of handling multiple users, however it is valid to use the + /// same path for both stores on a single session. + #[uniffi::constructor] + pub fn new(data_path: String, cache_path: String) -> Self { + Self { + paths: SessionPaths { data_path, cache_path }, + passphrase: Zeroizing::new(None), + pool_max_size: None, + cache_size: None, + journal_size_limit: None, + system_is_memory_constrained: false, + } + } - /// Set the cache size for the SQLite stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store exposes a SQLite connection. This method controls the cache - /// size, in **bytes (!)**. - /// - /// The cache represents data SQLite holds in memory at once per open - /// database file. The default cache implementation does not allocate the - /// full amount of cache memory all at once. Cache memory is allocated - /// in smaller chunks on an as-needed basis. - /// - /// See [`SqliteStoreConfig::cache_size`] to learn more. - pub fn cache_size(self: Arc, cache_size: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.cache_size = cache_size; - Arc::new(builder) - } + /// Set the passphrase for the stores given to + /// [`ClientBuilder::paths`]. + pub fn passphrase(self: Arc, passphrase: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.passphrase = Zeroizing::new(passphrase); + Arc::new(builder) + } + + /// Set the pool max size for the SQLite stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store exposes an async pool of connections. This method + /// controls the size of the pool. The larger the pool is, the + /// more memory is consumed, but also the more the app is + /// reactive because it doesn't need to wait on a pool to be + /// available to run queries. + /// + /// See [`SqliteStoreConfig::pool_max_size`] to learn more. + pub fn pool_max_size(self: Arc, pool_max_size: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.pool_max_size = pool_max_size.map(|size| { + size.try_into().expect("`pool_max_size` is too large to fit in `usize`") + }); + Arc::new(builder) + } + + /// Set the cache size for the SQLite stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store exposes a SQLite connection. This method controls the + /// cache size, in **bytes (!)**. + /// + /// The cache represents data SQLite holds in memory at once per open + /// database file. The default cache implementation does not allocate + /// the full amount of cache memory all at once. Cache memory is + /// allocated in smaller chunks on an as-needed basis. + /// + /// See [`SqliteStoreConfig::cache_size`] to learn more. + pub fn cache_size(self: Arc, cache_size: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.cache_size = cache_size; + Arc::new(builder) + } - /// Set the size limit for the SQLite WAL files of stores given to - /// [`ClientBuilder::session_paths`]. - /// - /// Each store uses the WAL journal mode. This method controls the size - /// limit of the WAL files, in **bytes (!)**. - /// - /// See [`SqliteStoreConfig::journal_size_limit`] to learn more. - pub fn journal_size_limit(self: Arc, limit: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.journal_size_limit = limit; - Arc::new(builder) + /// Set the size limit for the SQLite WAL files of stores given to + /// [`ClientBuilder::session_paths`]. + /// + /// Each store uses the WAL journal mode. This method controls the size + /// limit of the WAL files, in **bytes (!)**. + /// + /// See [`SqliteStoreConfig::journal_size_limit`] to learn more. + pub fn journal_size_limit(self: Arc, limit: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.journal_size_limit = limit; + Arc::new(builder) + } + + /// Tell the client that the system is memory constrained, like in a + /// push notification process for example. + /// + /// So far, at the time of writing (2025-04-07), it changes the defaults + /// of [`SqliteStoreConfig`], so one might not need to call + /// [`ClientBuilder::session_cache_size`] and siblings for example. + /// Please check [`SqliteStoreConfig::with_low_memory_config`]. + pub fn system_is_memory_constrained(self: Arc) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.system_is_memory_constrained = true; + Arc::new(builder) + } } - /// Tell the client that the system is memory constrained, like in a push - /// notification process for example. - /// - /// So far, at the time of writing (2025-04-07), it changes the defaults of - /// [`SqliteStoreConfig`], so one might not need to call - /// [`ClientBuilder::session_cache_size`] and siblings for example. Please - /// check [`SqliteStoreConfig::with_low_memory_config`]. - pub fn system_is_memory_constrained(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.system_is_memory_constrained = true; - Arc::new(builder) + impl SqliteSessionStoreBuilder { + pub fn build(&self) -> Result { + let data_path = Path::new(&self.paths.data_path); + let cache_path = Path::new(&self.paths.cache_path); + + debug!( + data_path = %data_path.to_string_lossy(), + cache_path = %cache_path.to_string_lossy(), + "Creating directories for data and cache stores.", + ); + + fs::create_dir_all(data_path)?; + fs::create_dir_all(cache_path)?; + + let mut sqlite_store_config = if self.system_is_memory_constrained { + SqliteStoreConfig::with_low_memory_config(data_path) + } else { + SqliteStoreConfig::new(data_path) + }; + + sqlite_store_config = sqlite_store_config.passphrase(self.passphrase.as_deref()); + + if let Some(size) = self.pool_max_size { + sqlite_store_config = sqlite_store_config.pool_max_size(size); + } + + if let Some(size) = self.cache_size { + sqlite_store_config = sqlite_store_config.cache_size(size); + } + + if let Some(limit) = self.journal_size_limit { + sqlite_store_config = sqlite_store_config.journal_size_limit(limit); + } + + Ok(SessionStoreResult::Sqlite { + config: sqlite_store_config, + store_path: data_path.to_owned(), + cache_path: cache_path.to_owned(), + }) + } } } #[cfg(feature = "indexeddb")] -#[derive(Clone, uniffi::Object)] -pub struct IndexedDbSessionStoreBuilder { - name: String, - passphrase: Option, -} +mod indexeddb_session_store { + #[derive(Clone, uniffi::Object)] + pub struct IndexedDbSessionStoreBuilder { + name: String, + passphrase: Option, + } -#[cfg(feature = "indexeddb")] -#[matrix_sdk_ffi_macros::export] -impl IndexedDbSessionStoreBuilder { - #[uniffi::constructor] - pub fn new(name: String) -> Arc { - Arc::new(Self { name, passphrase: None }) + #[matrix_sdk_ffi_macros::export] + impl IndexedDbSessionStoreBuilder { + #[uniffi::constructor] + pub fn new(name: String) -> Arc { + Arc::new(Self { name, passphrase: None }) + } + + /// Set the passphrase for the IndexedDB store. + pub fn passphrase(self: Arc, passphrase: Option) -> Arc { + let mut builder = unwrap_or_clone_arc(self); + builder.passphrase = passphrase; + Arc::new(builder) + } } - /// Set the passphrase for the IndexedDB store. - pub fn passphrase(self: Arc, passphrase: Option) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.passphrase = passphrase; - Arc::new(builder) + impl IndexedDbSessionStoreBuilder { + pub fn build(&self) -> Result { + Ok(SessionStoreResult::IndexedDb { + name: self.name.clone(), + passphrase: self.passphrase.clone(), + }) + } } } +#[cfg(feature = "indexeddb")] +pub use indexeddb_session_store::*; +#[cfg(feature = "sqlite")] +pub use sqlite_session_store::*; + +use crate::client_builder::ClientBuildError; + /// Internal struct for tracking the choice of session store config. #[derive(Clone)] pub enum SessionStoreConfig { @@ -159,64 +234,15 @@ pub enum SessionStoreConfig { IndexedDb(IndexedDbSessionStoreBuilder), } -/// The result of building a [`SessionStoreConfig`], with data that -/// can be passed directly to a ClientBuilder. -pub enum SessionStoreResult { - #[cfg(feature = "sqlite")] - Sqlite { config: SqliteStoreConfig, cache_path: PathBuf, store_path: PathBuf }, - #[cfg(feature = "indexeddb")] - IndexedDb { name: String, passphrase: Option }, -} impl SessionStoreConfig { pub(crate) fn build(&self) -> Result { match self { #[cfg(feature = "sqlite")] - SessionStoreConfig::Sqlite(config) => { - let data_path = Path::new(&config.paths.data_path); - let cache_path = Path::new(&config.paths.cache_path); - - debug!( - data_path = %data_path.to_string_lossy(), - cache_path = %cache_path.to_string_lossy(), - "Creating directories for data and cache stores.", - ); - - fs::create_dir_all(data_path)?; - fs::create_dir_all(cache_path)?; - - let mut sqlite_store_config = if config.system_is_memory_constrained { - SqliteStoreConfig::with_low_memory_config(data_path) - } else { - SqliteStoreConfig::new(data_path) - }; - - sqlite_store_config = sqlite_store_config.passphrase(config.passphrase.as_deref()); - - if let Some(size) = config.pool_max_size { - sqlite_store_config = sqlite_store_config.pool_max_size(size); - } - - if let Some(size) = config.cache_size { - sqlite_store_config = sqlite_store_config.cache_size(size); - } - - if let Some(limit) = config.journal_size_limit { - sqlite_store_config = sqlite_store_config.journal_size_limit(limit); - } - - Ok(SessionStoreResult::Sqlite { - config: sqlite_store_config, - store_path: data_path.to_owned(), - cache_path: cache_path.to_owned(), - }) - } + SessionStoreConfig::Sqlite(config) => config.build(), #[cfg(feature = "indexeddb")] - SessionStoreConfig::IndexedDb(config) => Ok(SessionStoreResult::IndexedDb { - name: config.name.clone(), - passphrase: config.passphrase.clone(), - }), + SessionStoreConfig::IndexedDb(config) => config.build(), } } } diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index edec278912d..551639ecf2a 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -231,7 +231,7 @@ fn check_clippy() -> Result<()> { "rustup run {NIGHTLY} cargo clippy --workspace --all-targets --exclude matrix-sdk-crypto --exclude xtask --no-default-features - --features native-tls,sso-login,testing + --features native-tls,sso-login,sqlite,testing -- -D warnings" ) .run()?; diff --git a/xtask/src/fixup.rs b/xtask/src/fixup.rs index edf753c51d1..b5e32957390 100644 --- a/xtask/src/fixup.rs +++ b/xtask/src/fixup.rs @@ -69,7 +69,7 @@ fn fix_clippy() -> Result<()> { "rustup run {NIGHTLY} cargo clippy --workspace --all-targets --fix --allow-dirty --allow-staged --exclude matrix-sdk-crypto --exclude xtask - --no-default-features --features native-tls,sso-login + --no-default-features --features native-tls,sqlite,sso-login -- -D warnings" ) .run()?; From 4cde4289d801e4fc63332cb309fee795a45bd142 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Fri, 20 Jun 2025 08:41:35 -0400 Subject: [PATCH 07/18] Cargo fmt --- bindings/matrix-sdk-ffi/src/session_store.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index 11e79182553..a88f6128b32 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -4,7 +4,6 @@ use std::path::PathBuf; #[cfg(feature = "sqlite")] use matrix_sdk::SqliteStoreConfig; - /// The result of building a [`SessionStoreConfig`], with data that /// can be passed directly to a ClientBuilder. pub enum SessionStoreResult { @@ -22,9 +21,8 @@ mod sqlite_session_store { use tracing::debug; use zeroize::Zeroizing; - use crate::helpers::unwrap_or_clone_arc; use super::SessionStoreResult; - use crate::client_builder::ClientBuildError; + use crate::{client_builder::ClientBuildError, helpers::unwrap_or_clone_arc}; /// The store paths the client will use when built. #[derive(Clone)] @@ -234,7 +232,6 @@ pub enum SessionStoreConfig { IndexedDb(IndexedDbSessionStoreBuilder), } - impl SessionStoreConfig { pub(crate) fn build(&self) -> Result { match self { From 15ea8846dbdb830e06901c73560cde86bfbd4e05 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Fri, 20 Jun 2025 09:00:36 -0400 Subject: [PATCH 08/18] Make constructor return Arc properly --- bindings/matrix-sdk-ffi/src/session_store.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index a88f6128b32..dd67c543451 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -55,15 +55,15 @@ mod sqlite_session_store { /// capable of handling multiple users, however it is valid to use the /// same path for both stores on a single session. #[uniffi::constructor] - pub fn new(data_path: String, cache_path: String) -> Self { - Self { + pub fn new(data_path: String, cache_path: String) -> Arc { + Arc::new(Self { paths: SessionPaths { data_path, cache_path }, passphrase: Zeroizing::new(None), pool_max_size: None, cache_size: None, journal_size_limit: None, system_is_memory_constrained: false, - } + }) } /// Set the passphrase for the stores given to From fe98e31533779fe99db359cec53559d584a254f6 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Fri, 20 Jun 2025 10:11:03 -0400 Subject: [PATCH 09/18] Add indexeddb includes --- bindings/matrix-sdk-ffi/src/session_store.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index dd67c543451..31dfacb482d 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -182,6 +182,11 @@ mod sqlite_session_store { #[cfg(feature = "indexeddb")] mod indexeddb_session_store { + use std::sync::Arc; + + use super::SessionStoreResult; + use crate::{client_builder::ClientBuildError, helpers::unwrap_or_clone_arc}; + #[derive(Clone, uniffi::Object)] pub struct IndexedDbSessionStoreBuilder { name: String, From ebebe34c91e8ba43afc1e788ec816f05061bf1fe Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 10:30:44 -0400 Subject: [PATCH 10/18] Update bindings/matrix-sdk-ffi/CHANGELOG.md Co-authored-by: Ivan Enderlin Signed-off-by: Daniel Salinas --- bindings/matrix-sdk-ffi/CHANGELOG.md | 29 ++++++++++++---------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/bindings/matrix-sdk-ffi/CHANGELOG.md b/bindings/matrix-sdk-ffi/CHANGELOG.md index 896daa451e1..0a62026de95 100644 --- a/bindings/matrix-sdk-ffi/CHANGELOG.md +++ b/bindings/matrix-sdk-ffi/CHANGELOG.md @@ -7,26 +7,21 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - ReleaseDate ### Features + - Configuration of the session store has been changed to allow for use of either IndexedDb or SQLite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. -Previously the code to configure a sqlite session would look something like this in a host language: -``` -builder - .sessionPaths("data_path", "cache_path") - .passphrase("foobar") -``` -With the new system, a helper object is exposed for either SQLite or IndexedDB to group those settings. -``` -builder - .session_store_sqlite( - SqliteSessionStoreBuilder.new({ dataPath: "data_path", cachePath: "cache_path" }) - .passphrase("foobar") - ) -``` - -The following methods from `ClientBuilder` have been moved onto `SqliteSessionStoreBuilder`: -`session_paths`, `session_passphrase`, `session_pool_max_size`, `session_cache_size`, and `session_journal_size_limit`. + Previously the code to configure a sqlite session would look something like this in a host language: + + \```rust + builder + .sessionPaths("data_path", "cache_path") + .passphrase("foobar") + \``` + + With the new system, a helper object is exposed for either SQLite or IndexedDB to group those settings. + +// etc ### Refactor From fc92feb1ceaf57d1176ce644f8194fbdb0324ff3 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 10:32:18 -0400 Subject: [PATCH 11/18] Update bindings/matrix-sdk-ffi/Cargo.toml Co-authored-by: Ivan Enderlin Signed-off-by: Daniel Salinas --- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index e296bd18efc..d7014561621 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -27,7 +27,7 @@ crate-type = [ default = ["bundled-sqlite", "unstable-msc4274"] bundled-sqlite = ["sqlite", "matrix-sdk/bundled-sqlite"] unstable-msc4274 = ["matrix-sdk-ui/unstable-msc4274"] -# Use indexeddb for session storage, supported only on Wasm platforms. +# Use IndexedDB for session storage, only supported on Wasm targets. indexeddb = ["matrix-sdk/indexeddb"] # Use sqlite for session storage, not supported on Wasm platforms. sqlite = ["matrix-sdk/sqlite"] From 4543b1b53181c0ca5681fddf27e3cffa7985d161 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 10:34:19 -0400 Subject: [PATCH 12/18] Update bindings/matrix-sdk-ffi/Cargo.toml Co-authored-by: Ivan Enderlin Signed-off-by: Daniel Salinas --- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index d7014561621..c25215f44af 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -29,7 +29,7 @@ bundled-sqlite = ["sqlite", "matrix-sdk/bundled-sqlite"] unstable-msc4274 = ["matrix-sdk-ui/unstable-msc4274"] # Use IndexedDB for session storage, only supported on Wasm targets. indexeddb = ["matrix-sdk/indexeddb"] -# Use sqlite for session storage, not supported on Wasm platforms. +# Use SQLite for session storage, not supported on Wasm targets. sqlite = ["matrix-sdk/sqlite"] # Required when targeting a Javascript environment, like Wasm in a browser. js = ["matrix-sdk-ui/js", "uuid/js"] From 69538a540eae9777b374db3d5137a3f05a6d0351 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 10:41:32 -0400 Subject: [PATCH 13/18] Update bindings/matrix-sdk-ffi/src/session_store.rs Co-authored-by: Ivan Enderlin Signed-off-by: Daniel Salinas --- bindings/matrix-sdk-ffi/src/session_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index 31dfacb482d..310577c2973 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -225,7 +225,7 @@ pub use sqlite_session_store::*; use crate::client_builder::ClientBuildError; -/// Internal struct for tracking the choice of session store config. +/// Represent the kind of store the client will configure. #[derive(Clone)] pub enum SessionStoreConfig { #[cfg(feature = "sqlite")] From 7987cc2fceaab0d7256cc6c5576fcfcefb010643 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 10:47:51 -0400 Subject: [PATCH 14/18] Code review feedback --- bindings/matrix-sdk-ffi/CHANGELOG.md | 13 ++++++++----- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- bindings/matrix-sdk-ffi/src/client_builder.rs | 11 ++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/bindings/matrix-sdk-ffi/CHANGELOG.md b/bindings/matrix-sdk-ffi/CHANGELOG.md index 0a62026de95..8c45e61cf95 100644 --- a/bindings/matrix-sdk-ffi/CHANGELOG.md +++ b/bindings/matrix-sdk-ffi/CHANGELOG.md @@ -9,19 +9,22 @@ All notable changes to this project will be documented in this file. ### Features - Configuration of the session store has been changed to allow for use of either IndexedDb -or SQLite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. + or SQLite on relevant platforms. These can be enabled via features, `indexeddb` or `sqlite`. Previously the code to configure a sqlite session would look something like this in a host language: - \```rust + ```rust builder - .sessionPaths("data_path", "cache_path") + .session_paths("data_path", "cache_path") .passphrase("foobar") - \``` + ``` With the new system, a helper object is exposed for either SQLite or IndexedDB to group those settings. -// etc + ```rust + builder + .session_store_sqlite(SqliteSessionStoreBuilder::new("data_path", "cache_path").passphrase("foobar")) + ``` ### Refactor diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index c25215f44af..356e2eddc41 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -32,7 +32,7 @@ indexeddb = ["matrix-sdk/indexeddb"] # Use SQLite for session storage, not supported on Wasm targets. sqlite = ["matrix-sdk/sqlite"] # Required when targeting a Javascript environment, like Wasm in a browser. -js = ["matrix-sdk-ui/js", "uuid/js"] +js = ["matrix-sdk-ui/js"] # Use the TLS implementation provided by the host system, necessary on iOS and Wasm platforms. native-tls = ["matrix-sdk/native-tls", "sentry?/native-tls"] # Use Rustls as the TLS implementation, necessary on Android platforms. diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index 8c7a569d550..f430e46779a 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -350,23 +350,24 @@ impl ClientBuilder { inner_builder.cross_process_store_locks_holder_name(holder_name.clone()); } - let mut store_path = None; - if let Some(session_store) = builder.session_store { + let store_path = if let Some(session_store) = builder.session_store { match session_store.build()? { #[cfg(feature = "indexeddb")] SessionStoreResult::IndexedDb { name, passphrase } => { inner_builder = inner_builder.indexeddb_store(&name, passphrase.as_deref()); + None } #[cfg(feature = "sqlite")] SessionStoreResult::Sqlite { config, cache_path, store_path: data_path } => { inner_builder = inner_builder .sqlite_store_with_config_and_cache_path(config, Some(cache_path)); - store_path = Some(data_path); + Some(data_path) } } } else { - debug!("Not using a session store.") - } + debug!("Not using a session store."); + None + }; // Determine server either from URL, server name or user ID. inner_builder = match builder.homeserver_cfg { From 1d844de6bb3b3abf9f4e8ccf6d8b36a755b3491b Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 11:02:03 -0400 Subject: [PATCH 15/18] Rename builder --- bindings/matrix-sdk-ffi/src/client_builder.rs | 8 ++++---- bindings/matrix-sdk-ffi/src/session_store.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index f430e46779a..8ea9f3edc6b 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -25,7 +25,7 @@ use crate::{ helpers::unwrap_or_clone_arc, qr_code::{HumanQrLoginError, QrCodeData, QrLoginProgressListener}, runtime::get_runtime_handle, - session_store::{SessionStoreConfig, SessionStoreResult}, + session_store::{SessionStoreBuilder, SessionStoreResult}, task_handle::TaskHandle, }; @@ -108,7 +108,7 @@ impl From for ClientBuildError { #[derive(Clone, uniffi::Object)] pub struct ClientBuilder { - session_store: Option, + session_store: Option, system_is_memory_constrained: bool, username: Option, homeserver_cfg: Option, @@ -554,7 +554,7 @@ impl ClientBuilder { config: Arc, ) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.session_store = Some(SessionStoreConfig::Sqlite(config.as_ref().clone())); + builder.session_store = Some(SessionStoreBuilder::Sqlite(config.as_ref().clone())); Arc::new(builder) } } @@ -568,7 +568,7 @@ impl ClientBuilder { config: Arc, ) -> Arc { let mut builder = unwrap_or_clone_arc(self); - builder.session_store = Some(SessionStoreConfig::IndexedDb(config.as_ref().clone())); + builder.session_store = Some(SessionStoreBuilder::IndexedDb(config.as_ref().clone())); Arc::new(builder) } } diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index 310577c2973..685e7265fc7 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; #[cfg(feature = "sqlite")] use matrix_sdk::SqliteStoreConfig; -/// The result of building a [`SessionStoreConfig`], with data that +/// The result of building a [`SessionStoreBuilder`], with data that /// can be passed directly to a ClientBuilder. pub enum SessionStoreResult { #[cfg(feature = "sqlite")] @@ -227,24 +227,24 @@ use crate::client_builder::ClientBuildError; /// Represent the kind of store the client will configure. #[derive(Clone)] -pub enum SessionStoreConfig { +pub enum SessionStoreBuilder { + /// Represents the builder for the SQLite store. #[cfg(feature = "sqlite")] - /// Setup the client to use the SQLite store. Sqlite(SqliteSessionStoreBuilder), + /// Represents the client for the IndexedDB store. #[cfg(feature = "indexeddb")] - /// Setup the client to use the IndexedDB store. IndexedDb(IndexedDbSessionStoreBuilder), } -impl SessionStoreConfig { +impl SessionStoreBuilder { pub(crate) fn build(&self) -> Result { match self { #[cfg(feature = "sqlite")] - SessionStoreConfig::Sqlite(config) => config.build(), + SessionStoreBuilder::Sqlite(config) => config.build(), #[cfg(feature = "indexeddb")] - SessionStoreConfig::IndexedDb(config) => config.build(), + SessionStoreBuilder::IndexedDb(config) => config.build(), } } } From ce33b96ac2c32e088974abc820ef89cbf1a865dc Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 12:23:52 -0400 Subject: [PATCH 16/18] Adjust language in the README --- bindings/matrix-sdk-ffi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/README.md b/bindings/matrix-sdk-ffi/README.md index 114d4055d06..0f68e8b4d28 100644 --- a/bindings/matrix-sdk-ffi/README.md +++ b/bindings/matrix-sdk-ffi/README.md @@ -12,8 +12,8 @@ Given the number of platforms targeted, we have broken out a number of features ### Functionality - `sentry`: Enable error monitoring using Sentry, not supports on Wasm platforms. - `bundled-sqlite`: Use an embedded version of sqlite instead of the system provided one. -- `sqlite`: Use sqlite for session storage, not available on Wasm platforms. -- `indexeddb`: Use IndexedDb for session storage, only available on Wasm platforms. +- `sqlite`: Use SQLite for session storage, not supported on Wasm targets. +- `indexeddb`: Use IndexedDB for session storage, only supported on Wasm targets. ### Unstable specs - `unstable-msc4274`: Adds support for gallery message types, which contain multiple media elements. From 90048a8f49391b9f0eb28e84dc2403b52ed98b03 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Mon, 23 Jun 2025 12:29:22 -0400 Subject: [PATCH 17/18] Make it an xor --- bindings/matrix-sdk-ffi/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/build.rs b/bindings/matrix-sdk-ffi/build.rs index c28765bf36d..eb844e4cbe0 100644 --- a/bindings/matrix-sdk-ffi/build.rs +++ b/bindings/matrix-sdk-ffi/build.rs @@ -78,8 +78,8 @@ fn main() -> Result<(), Box> { let sqlite_set = env_is_set("CARGO_FEATURE_SQLITE"); let indexeddb_set = env_is_set("CARGO_FEATURE_INDEXEDDB"); ensure( - sqlite_set || indexeddb_set, - "one of the features 'sqlite' or 'indexeddb' must be enabled", + sqlite_set ^ indexeddb_set, + "one of the features 'sqlite' or (exclusive) 'indexeddb' must be enabled", ); setup_x86_64_android_workaround(); uniffi::generate_scaffolding("./src/api.udl").expect("Building the UDL file failed"); From 5c93af9a317d81523592aa70c938e2ac18e56e13 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Tue, 24 Jun 2025 06:59:55 -0400 Subject: [PATCH 18/18] Use Self instead --- bindings/matrix-sdk-ffi/src/session_store.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_store.rs b/bindings/matrix-sdk-ffi/src/session_store.rs index 685e7265fc7..cada59c65a5 100644 --- a/bindings/matrix-sdk-ffi/src/session_store.rs +++ b/bindings/matrix-sdk-ffi/src/session_store.rs @@ -241,10 +241,10 @@ impl SessionStoreBuilder { pub(crate) fn build(&self) -> Result { match self { #[cfg(feature = "sqlite")] - SessionStoreBuilder::Sqlite(config) => config.build(), + Self::Sqlite(config) => config.build(), #[cfg(feature = "indexeddb")] - SessionStoreBuilder::IndexedDb(config) => config.build(), + Self::IndexedDb(config) => config.build(), } } }