feat(indexeddb): expose struct and fns for opening all stores

Signed-off-by: Michael Goldenberg <m@mgoldenberg.net>
This commit is contained in:
Michael Goldenberg
2025-12-07 22:56:28 -05:00
committed by Ivan Enderlin
parent 2bec882c83
commit e74bf33178

View File

@@ -25,6 +25,151 @@ pub use state_store::{
MigrationConflictStrategy,
};
#[cfg(feature = "event-cache-store")]
pub use crate::event_cache_store::{
IndexeddbEventCacheStore, IndexeddbEventCacheStoreBuilder, IndexeddbEventCacheStoreError,
};
#[cfg(feature = "media-store")]
pub use crate::media_store::{
IndexeddbMediaStore, IndexeddbMediaStoreBuilder, IndexeddbMediaStoreError,
};
/// Structure containing implementations of every type
/// of store using IndexedDB for persistent storage.
///
/// Note that each of the stores is behind a feature flag and will
/// only be available when its corresponding flag is set.
pub struct IndexeddbStores {
/// An IndexedDB-backed implementation of [`CryptoStore`][1]
///
/// [1]: matrix_sdk_crypto::store::CryptoStore
#[cfg(feature = "e2e-encryption")]
pub crypto: IndexeddbCryptoStore,
/// An IndexedDB-backed implementation of [`StateStore`][1]
///
/// [1]: matrix_sdk_base::store::StateStore
#[cfg(feature = "state-store")]
pub state: IndexeddbStateStore,
/// An IndexedDB-backed implementation of [`EventCacheStore`][1]
///
/// [1]: matrix_sdk_base::event_cache::store::EventCacheStore
#[cfg(feature = "event-cache-store")]
pub event_cache: IndexeddbEventCacheStore,
/// An IndexedDB-backed implementation of [`MediaStore`][1]
///
/// [1]: matrix_sdk_base::media::store::MediaStore
#[cfg(feature = "media-store")]
pub media: IndexeddbMediaStore,
}
impl IndexeddbStores {
/// Opens and returns all stores using the given database name and,
/// optionally, a passphrase.
///
/// If `e2e-encryption` and `state-store` features are not enabled,
/// `passphrase` is ignored. Otherwise `passphrase` is used to import or
/// create a [`StoreCipher`][1] which encrypts contents of all stores.
///
/// Note that each of the stores is behind a feature flag and will only be
/// opened when the corresponding flag is set.
///
/// [1]: matrix_sdk_store_encryption::StoreCipher
pub async fn open(
name: &str,
#[allow(unused_variables)] passphrase: Option<&str>,
) -> Result<Self, OpenStoreError> {
#[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
if let Some(passphrase) = passphrase {
return Self::open_with_passphrase(name, passphrase).await;
}
Self::open_without_passphrase(name).await
}
/// Opens and returns all stores using the given database name. Contents of
/// the stores are NOT encrypted.
///
/// Note that each of the stores is behind a feature flag and will only be
/// opened when the corresponding flag is set.
#[allow(clippy::unused_async)]
pub async fn open_without_passphrase(name: &str) -> Result<Self, OpenStoreError> {
#[cfg(feature = "state-store")]
let state = IndexeddbStateStore::builder()
.name(name.to_owned())
.build()
.await
.map_err(StoreError::from)?;
#[cfg(feature = "e2e-encryption")]
let crypto = IndexeddbCryptoStore::open_with_name(name).await?;
#[cfg(feature = "event-cache-store")]
let event_cache = IndexeddbEventCacheStoreBuilder::with_prefix(name).build().await?;
#[cfg(feature = "media-store")]
let media = IndexeddbMediaStoreBuilder::with_prefix(name).build().await?;
Ok(Self {
#[cfg(feature = "state-store")]
state,
#[cfg(feature = "e2e-encryption")]
crypto,
#[cfg(feature = "event-cache-store")]
event_cache,
#[cfg(feature = "media-store")]
media,
})
}
/// Opens and returns all stores using the given database name and
/// passphrase. Passphrase is used to import or create a [`StoreCipher`][1]
/// which encrypts contents of all stores.
///
/// Note that [`IndexeddbEventCacheStore`] and [`IndexeddbMediaStore`] are
/// behind feature flags and will only be opened when their
/// corresponding flags are set.
///
/// [1]: matrix_sdk_store_encryption::StoreCipher
#[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
pub async fn open_with_passphrase(
name: &str,
passphrase: &str,
) -> Result<Self, OpenStoreError> {
let state = IndexeddbStateStore::builder()
.name(name.to_owned())
.passphrase(passphrase.to_owned())
.build()
.await
.map_err(StoreError::from)?;
let store_cipher =
state.store_cipher.clone().ok_or(OpenStoreError::FailedToLoadStoreCipher)?;
let crypto =
IndexeddbCryptoStore::open_with_store_cipher(name, Some(store_cipher.clone())).await?;
#[cfg(feature = "event-cache-store")]
let event_cache = IndexeddbEventCacheStoreBuilder::with_prefix(name)
.store_cipher(store_cipher.clone())
.build()
.await?;
#[cfg(feature = "media-store")]
let media = IndexeddbMediaStoreBuilder::with_prefix(name)
.store_cipher(store_cipher.clone())
.build()
.await?;
Ok(Self {
state,
crypto,
#[cfg(feature = "event-cache-store")]
event_cache,
#[cfg(feature = "media-store")]
media,
})
}
}
/// Create a [`IndexeddbStateStore`] and a [`IndexeddbCryptoStore`] that use the
/// same name and passphrase.
#[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
@@ -75,4 +220,22 @@ pub enum OpenStoreError {
#[cfg(feature = "e2e-encryption")]
#[error(transparent)]
Crypto(#[from] IndexeddbCryptoStoreError),
/// An error occurred while trying to load a [`StoreCipher`][1] from a
/// passphrase
///
/// [1]: matrix_sdk_store_encryption::StoreCipher
#[cfg(feature = "e2e-encryption")]
#[error("failed to load store cipher")]
FailedToLoadStoreCipher,
/// An error occurred with the event cache store implementation.
#[cfg(feature = "event-cache-store")]
#[error(transparent)]
Event(#[from] IndexeddbEventCacheStoreError),
/// An error occurred with the media store implementation.
#[cfg(feature = "media-store")]
#[error(transparent)]
Media(#[from] IndexeddbMediaStoreError),
}