matrix_sdk_indexeddb/
lib.rs

1#![cfg_attr(not(target_family = "wasm"), allow(unused))]
2
3#[cfg(feature = "state-store")]
4use matrix_sdk_base::store::StoreError;
5use thiserror::Error;
6
7#[cfg(feature = "e2e-encryption")]
8mod crypto_store;
9mod error;
10#[cfg(feature = "event-cache-store")]
11mod event_cache_store;
12#[cfg(feature = "media-store")]
13mod media_store;
14mod serializer;
15#[cfg(feature = "state-store")]
16mod state_store;
17#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
18mod transaction;
19
20#[cfg(feature = "e2e-encryption")]
21pub use crypto_store::{IndexeddbCryptoStore, IndexeddbCryptoStoreError};
22#[cfg(feature = "state-store")]
23pub use state_store::{
24    IndexeddbStateStore, IndexeddbStateStoreBuilder, IndexeddbStateStoreError,
25    MigrationConflictStrategy,
26};
27
28#[cfg(feature = "event-cache-store")]
29pub use crate::event_cache_store::{
30    IndexeddbEventCacheStore, IndexeddbEventCacheStoreBuilder, IndexeddbEventCacheStoreError,
31};
32#[cfg(feature = "media-store")]
33pub use crate::media_store::{
34    IndexeddbMediaStore, IndexeddbMediaStoreBuilder, IndexeddbMediaStoreError,
35};
36
37/// Structure containing implementations of every type
38/// of store using IndexedDB for persistent storage.
39///
40/// Note that each of the stores is behind a feature flag and will
41/// only be available when its corresponding flag is set.
42pub struct IndexeddbStores {
43    /// An IndexedDB-backed implementation of [`CryptoStore`][1]
44    ///
45    /// [1]: matrix_sdk_crypto::store::CryptoStore
46    #[cfg(feature = "e2e-encryption")]
47    pub crypto: IndexeddbCryptoStore,
48    /// An IndexedDB-backed implementation of [`StateStore`][1]
49    ///
50    /// [1]: matrix_sdk_base::store::StateStore
51    #[cfg(feature = "state-store")]
52    pub state: IndexeddbStateStore,
53    /// An IndexedDB-backed implementation of [`EventCacheStore`][1]
54    ///
55    /// [1]: matrix_sdk_base::event_cache::store::EventCacheStore
56    #[cfg(feature = "event-cache-store")]
57    pub event_cache: IndexeddbEventCacheStore,
58    /// An IndexedDB-backed implementation of [`MediaStore`][1]
59    ///
60    /// [1]: matrix_sdk_base::media::store::MediaStore
61    #[cfg(feature = "media-store")]
62    pub media: IndexeddbMediaStore,
63}
64
65impl IndexeddbStores {
66    /// Opens and returns all stores using the given database name and,
67    /// optionally, a passphrase.
68    ///
69    /// If `e2e-encryption` and `state-store` features are not enabled,
70    /// `passphrase` is ignored. Otherwise `passphrase` is used to import or
71    /// create a [`StoreCipher`][1] which encrypts contents of all stores.
72    ///
73    /// Note that each of the stores is behind a feature flag and will only be
74    /// opened when the corresponding flag is set.
75    ///
76    /// [1]: matrix_sdk_store_encryption::StoreCipher
77    pub async fn open(
78        name: &str,
79        #[allow(unused_variables)] passphrase: Option<&str>,
80    ) -> Result<Self, OpenStoreError> {
81        #[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
82        if let Some(passphrase) = passphrase {
83            return Self::open_with_passphrase(name, passphrase).await;
84        }
85
86        Self::open_without_passphrase(name).await
87    }
88
89    /// Opens and returns all stores using the given database name. Contents of
90    /// the stores are NOT encrypted.
91    ///
92    /// Note that each of the stores is behind a feature flag and will only be
93    /// opened when the corresponding flag is set.
94    #[allow(clippy::unused_async)]
95    pub async fn open_without_passphrase(name: &str) -> Result<Self, OpenStoreError> {
96        #[cfg(feature = "state-store")]
97        let state = IndexeddbStateStore::builder()
98            .name(name.to_owned())
99            .build()
100            .await
101            .map_err(StoreError::from)?;
102
103        #[cfg(feature = "e2e-encryption")]
104        let crypto = IndexeddbCryptoStore::open_with_name(name).await?;
105
106        #[cfg(feature = "event-cache-store")]
107        let event_cache = IndexeddbEventCacheStoreBuilder::with_prefix(name).build().await?;
108
109        #[cfg(feature = "media-store")]
110        let media = IndexeddbMediaStoreBuilder::with_prefix(name).build().await?;
111
112        Ok(Self {
113            #[cfg(feature = "state-store")]
114            state,
115            #[cfg(feature = "e2e-encryption")]
116            crypto,
117            #[cfg(feature = "event-cache-store")]
118            event_cache,
119            #[cfg(feature = "media-store")]
120            media,
121        })
122    }
123
124    /// Opens and returns all stores using the given database name and
125    /// passphrase. Passphrase is used to import or create a [`StoreCipher`][1]
126    /// which encrypts contents of all stores.
127    ///
128    /// Note that [`IndexeddbEventCacheStore`] and [`IndexeddbMediaStore`] are
129    /// behind feature flags and will only be opened when their
130    /// corresponding flags are set.
131    ///
132    /// [1]: matrix_sdk_store_encryption::StoreCipher
133    #[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
134    pub async fn open_with_passphrase(
135        name: &str,
136        passphrase: &str,
137    ) -> Result<Self, OpenStoreError> {
138        let state = IndexeddbStateStore::builder()
139            .name(name.to_owned())
140            .passphrase(passphrase.to_owned())
141            .build()
142            .await
143            .map_err(StoreError::from)?;
144        let store_cipher =
145            state.store_cipher.clone().ok_or(OpenStoreError::FailedToLoadStoreCipher)?;
146
147        let crypto =
148            IndexeddbCryptoStore::open_with_store_cipher(name, Some(store_cipher.clone())).await?;
149
150        #[cfg(feature = "event-cache-store")]
151        let event_cache = IndexeddbEventCacheStoreBuilder::with_prefix(name)
152            .store_cipher(store_cipher.clone())
153            .build()
154            .await?;
155
156        #[cfg(feature = "media-store")]
157        let media = IndexeddbMediaStoreBuilder::with_prefix(name)
158            .store_cipher(store_cipher.clone())
159            .build()
160            .await?;
161
162        Ok(Self {
163            state,
164            crypto,
165            #[cfg(feature = "event-cache-store")]
166            event_cache,
167            #[cfg(feature = "media-store")]
168            media,
169        })
170    }
171}
172
173/// Create a [`IndexeddbStateStore`] and a [`IndexeddbCryptoStore`] that use the
174/// same name and passphrase.
175#[deprecated(
176    note = "this function only opens state and crypto stores, use `IndexeddbStores::open()` instead."
177)]
178#[cfg(all(feature = "e2e-encryption", feature = "state-store"))]
179pub async fn open_stores_with_name(
180    name: &str,
181    passphrase: Option<&str>,
182) -> Result<(IndexeddbStateStore, IndexeddbCryptoStore), OpenStoreError> {
183    let mut builder = IndexeddbStateStore::builder().name(name.to_owned());
184    if let Some(passphrase) = passphrase {
185        builder = builder.passphrase(passphrase.to_owned());
186    }
187
188    let state_store = builder.build().await.map_err(StoreError::from)?;
189    let crypto_store =
190        IndexeddbCryptoStore::open_with_store_cipher(name, state_store.store_cipher.clone())
191            .await?;
192
193    Ok((state_store, crypto_store))
194}
195
196/// Create an [`IndexeddbStateStore`].
197///
198/// If a `passphrase` is given, the store will be encrypted using a key derived
199/// from that passphrase.
200#[cfg(feature = "state-store")]
201pub async fn open_state_store(
202    name: &str,
203    passphrase: Option<&str>,
204) -> Result<IndexeddbStateStore, OpenStoreError> {
205    let mut builder = IndexeddbStateStore::builder().name(name.to_owned());
206    if let Some(passphrase) = passphrase {
207        builder = builder.passphrase(passphrase.to_owned());
208    }
209    let state_store = builder.build().await.map_err(StoreError::from)?;
210
211    Ok(state_store)
212}
213
214/// All the errors that can occur when opening an IndexedDB store.
215#[derive(Error, Debug)]
216pub enum OpenStoreError {
217    /// An error occurred with the state store implementation.
218    #[cfg(feature = "state-store")]
219    #[error(transparent)]
220    State(#[from] StoreError),
221
222    /// An error occurred with the crypto store implementation.
223    #[cfg(feature = "e2e-encryption")]
224    #[error(transparent)]
225    Crypto(#[from] IndexeddbCryptoStoreError),
226
227    /// An error occurred while trying to load a [`StoreCipher`][1] from a
228    /// passphrase
229    ///
230    /// [1]: matrix_sdk_store_encryption::StoreCipher
231    #[cfg(feature = "e2e-encryption")]
232    #[error("failed to load store cipher")]
233    FailedToLoadStoreCipher,
234
235    /// An error occurred with the event cache store implementation.
236    #[cfg(feature = "event-cache-store")]
237    #[error(transparent)]
238    Event(#[from] IndexeddbEventCacheStoreError),
239
240    /// An error occurred with the media store implementation.
241    #[cfg(feature = "media-store")]
242    #[error(transparent)]
243    Media(#[from] IndexeddbMediaStoreError),
244}