atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
crates/matrix-sdk-indexeddb/src/lib.rsRust244 lines8.5 KB
1
#![cfg_attr(not(target_family = "wasm"), allow(unused))]
2
3
#[cfg(feature = "state-store")]
4
use matrix_sdk_base::store::StoreError;
5
use thiserror::Error;
6
7
#[cfg(feature = "e2e-encryption")]
8
mod crypto_store;
9
mod error;
10
#[cfg(feature = "event-cache-store")]
11
mod event_cache_store;
12
#[cfg(feature = "media-store")]
13
mod media_store;
14
mod serializer;
15
#[cfg(feature = "state-store")]
16
mod state_store;
17
#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
18
mod transaction;
19
20
#[cfg(feature = "e2e-encryption")]
21
pub use crypto_store::{IndexeddbCryptoStore, IndexeddbCryptoStoreError};
22
#[cfg(feature = "state-store")]
23
pub use state_store::{
24
    IndexeddbStateStore, IndexeddbStateStoreBuilder, IndexeddbStateStoreError,
25
    MigrationConflictStrategy,
26
};
27
28
#[cfg(feature = "event-cache-store")]
29
pub use crate::event_cache_store::{
30
    IndexeddbEventCacheStore, IndexeddbEventCacheStoreBuilder, IndexeddbEventCacheStoreError,
31
};
32
#[cfg(feature = "media-store")]
33
pub 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.
42
pub 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
65
impl 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"))]
179
pub 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")]
201
pub 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)]
216
pub 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
}