diff --git a/Cargo.toml b/Cargo.toml index 67f41667a..5ebe9edb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,4 @@ [workspace] members = [ - "crates/matrix-qrcode", - "crates/matrix-sdk", - "crates/matrix-sdk-appservice", - "crates/matrix-sdk-base", - "crates/matrix-sdk-common", - "crates/matrix-sdk-crypto", - "crates/matrix-sdk-test", - "crates/matrix-sdk-test-macros", + "crates/*", ] diff --git a/crates/matrix-sdk-base/Cargo.toml b/crates/matrix-sdk-base/Cargo.toml index 35653b1bd..d830a11c7 100644 --- a/crates/matrix-sdk-base/Cargo.toml +++ b/crates/matrix-sdk-base/Cargo.toml @@ -22,16 +22,21 @@ qrcode = ["matrix-sdk-crypto/qrcode"] sled_state_store = [ "sled", "tokio", + "state_key", +] +sled_cryptostore = ["matrix-sdk-crypto/sled_cryptostore"] + +indexeddb_state_store = ["state_key"] +indexeddb_cryptostore = ["matrix-sdk-crypto/indexeddb_cryptostore"] + +# State Key is a helper feature for state store implementations +state_key = [ "pbkdf2", "hmac", "sha2", "rand", "chacha20poly1305", ] -sled_cryptostore = ["matrix-sdk-crypto/sled_cryptostore"] - -indexeddb_state_store = ["indexed_db_futures", "wasm-bindgen", "pbkdf2", "hmac", "sha2", "rand", "chacha20poly1305"] -indexeddb_cryptostore = ["matrix-sdk-crypto/indexeddb_cryptostore"] [dependencies] chacha20poly1305 = { version = "0.9.0", optional = true } @@ -51,14 +56,11 @@ sled = { version = "0.34.6", optional = true } thiserror = "1.0.25" tracing = "0.1.26" zeroize = { version = "1.3.0", features = ["zeroize_derive"] } +anyhow = "1" ## Feature sled_state_store tokio = { version = "1.7.1", optional = true, default-features = false, features = ["sync", "fs"] } -## Feature indexeddb-state-store -indexed_db_futures = { version = "0.2.0", optional = true } -wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true } - [dependencies.ruma] git = "https://github.com/ruma/ruma/" rev = "37095f88553b311e7a70adaaabe39976fb8ff71c" diff --git a/crates/matrix-sdk-base/src/lib.rs b/crates/matrix-sdk-base/src/lib.rs index fa8439170..444d4cbd7 100644 --- a/crates/matrix-sdk-base/src/lib.rs +++ b/crates/matrix-sdk-base/src/lib.rs @@ -15,9 +15,9 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] #![deny( missing_debug_implementations, - missing_docs, trivial_casts, trivial_numeric_casts, unused_extern_crates, @@ -46,7 +46,7 @@ mod error; pub mod media; mod rooms; mod session; -mod store; +pub mod store; pub use client::{BaseClient, BaseClientConfig}; #[cfg(feature = "encryption")] diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index 499cca028..05cb226b7 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// Implementing the state store + #[cfg(feature = "sled_state_store")] use std::path::Path; use std::{ @@ -20,7 +22,7 @@ use std::{ sync::Arc, }; -#[cfg(test)] +#[cfg(any(test, feature = "testing"))] #[macro_use] pub mod integration_tests; @@ -40,15 +42,11 @@ use ruma::{ EventId, MxcUri, RoomId, UserId, }; -#[cfg(any(feature = "sled_state_store", feature = "indexeddb_state_store"))] -mod store_key; +pub mod store_key; #[cfg(feature = "sled_state_store")] use sled::Db; -#[cfg(feature = "indexeddb_state_store")] -mod indexeddb_store; - use crate::{ deserialized_responses::{MemberEvent, StrippedMemberEvent}, media::MediaRequest, @@ -61,8 +59,6 @@ mod memory_store; #[cfg(feature = "sled_state_store")] mod sled_store; -#[cfg(feature = "indexeddb_state_store")] -use self::indexeddb_store::IndexeddbStore; #[cfg(not(any(feature = "sled_state_store", feature = "indexeddb_state_store")))] use self::memory_store::MemoryStore; #[cfg(feature = "sled_state_store")] @@ -71,21 +67,9 @@ use self::sled_store::SledStore; /// State store specific error type. #[derive(Debug, thiserror::Error)] pub enum StoreError { - /// An error happened in the underlying sled database. - #[cfg(feature = "sled_state_store")] #[error(transparent)] - Sled(#[from] sled::Error), - /// An error happened in the underlying Indexed Database. - #[cfg(feature = "indexeddb_state_store")] - #[error("IndexDB error: {name} ({code}): {message}")] - Indexeddb { - /// DomException code - code: u16, - /// Specific name of the DomException - name: String, - /// Message given to the DomException - message: String, - }, + /// An error happened in the underlying sled database. + Backend(#[from] anyhow::Error), /// An error happened while serializing or deserializing some data. #[error(transparent)] Json(#[from] serde_json::Error), @@ -111,14 +95,6 @@ pub enum StoreError { #[error(transparent)] Task(#[from] tokio::task::JoinError), } - -#[cfg(feature = "indexeddb_state_store")] -impl From for StoreError { - fn from(frm: indexed_db_futures::web_sys::DomException) -> StoreError { - StoreError::Indexeddb { name: frm.name(), message: frm.message(), code: frm.code() } - } -} - /// A `StateStore` specific result type. pub type Result = std::result::Result; diff --git a/crates/matrix-sdk-common/src/lib.rs b/crates/matrix-sdk-common/src/lib.rs index 43fbf53fa..217f3182e 100644 --- a/crates/matrix-sdk-common/src/lib.rs +++ b/crates/matrix-sdk-common/src/lib.rs @@ -10,6 +10,7 @@ pub use async_trait::async_trait; pub use instant; +pub use ruma; pub mod deserialized_responses; pub mod executor; diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml new file mode 100644 index 000000000..b1d3bb3cb --- /dev/null +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "matrix-sdk-indexeddb" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +matrix-sdk-base = { path = "../matrix-sdk-base", features = ["state_key"] } +matrix-sdk-common = { path = "../matrix-sdk-common" } +thiserror = "1.0.25" +anyhow = "1" + +indexed_db_futures = { version = "0.2.0" } +wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } +serde = { version = "1.0.126" } +serde_json = "1.0.64" diff --git a/crates/matrix-sdk-indexeddb/src/lib.rs b/crates/matrix-sdk-indexeddb/src/lib.rs new file mode 100644 index 000000000..612aae77f --- /dev/null +++ b/crates/matrix-sdk-indexeddb/src/lib.rs @@ -0,0 +1,5 @@ +#[cfg(not(target_arch="wasm32"))] +compile_error!("indexeddb is only support on wasm32. You may want to add --target wasm32-unknown-unknown"); + + +pub mod state_store; \ No newline at end of file diff --git a/crates/matrix-sdk-base/src/store/indexeddb_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs similarity index 85% rename from crates/matrix-sdk-base/src/store/indexeddb_store.rs rename to crates/matrix-sdk-indexeddb/src/state_store.rs index fe54a743b..a3e2b56aa 100644 --- a/crates/matrix-sdk-base/src/store/indexeddb_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -15,27 +15,32 @@ use std::collections::BTreeSet; use indexed_db_futures::prelude::*; -use matrix_sdk_common::{async_trait, SafeEncode}; -use ruma::{ - events::{ - presence::PresenceEvent, - receipt::Receipt, - room::member::{MembershipState, RoomMemberEventContent}, - AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent, EventType, - }, - receipt::ReceiptType, - serde::Raw, - EventId, MxcUri, RoomId, UserId, +use matrix_sdk_common::{async_trait, SafeEncode, + ruma::{ + events::{ + presence::PresenceEvent, + receipt::Receipt, + room::member::{MembershipState, RoomMemberEventContent}, + AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent, EventType, + }, + receipt::ReceiptType, + serde::Raw, + EventId, MxcUri, RoomId, UserId, + } }; use serde::{Deserialize, Serialize}; use wasm_bindgen::JsValue; -use self::store_key::{EncryptedEvent, StoreKey}; -use super::{store_key, Result, RoomInfo, StateChanges, StateStore, StoreError}; -use crate::{ +use matrix_sdk_base::{ + store::{ + store_key::{self, EncryptedEvent, StoreKey}, + Result as StoreResult, StateChanges, StateStore, StoreError + }, + RoomInfo, deserialized_responses::MemberEvent, media::{MediaRequest, UniqueKey}, }; +use anyhow::anyhow; #[derive(Debug, Serialize, Deserialize)] pub enum DatabaseType { @@ -49,11 +54,41 @@ pub enum SerializationError { Json(#[from] serde_json::Error), #[error(transparent)] Encryption(#[from] store_key::Error), + #[error("DomException {name} ({code}): {message}")] + DomException { + name: String, + message: String, + code: u16, + }, + #[error(transparent)] + StoreError(#[from] StoreError), +} + +impl From for SerializationError { + fn from(frm: indexed_db_futures::web_sys::DomException) -> SerializationError { + SerializationError::DomException { name: frm.name(), message: frm.message(), code: frm.code() } + } +} + +impl From for StoreError { + fn from(e: SerializationError) -> Self { + match e { + SerializationError::Json(e) => StoreError::Json(e), + SerializationError::StoreError(e) => e, + SerializationError::Encryption(e) => match e { + store_key::Error::Random(e) => StoreError::Encryption(e.to_string()), + store_key::Error::Serialization(e) => StoreError::Json(e), + store_key::Error::Encryption(e) => StoreError::Encryption(e), + }, + _ => { + StoreError::Backend(anyhow!(e)) + } + } + } } #[allow(non_snake_case)] mod KEYS { - // STORES pub const SESSION: &'static str = "session"; @@ -88,18 +123,6 @@ mod KEYS { pub const SYNC_TOKEN: &'static str = "sync_token"; } -impl From for StoreError { - fn from(e: SerializationError) -> Self { - match e { - SerializationError::Json(e) => StoreError::Json(e), - SerializationError::Encryption(e) => match e { - store_key::Error::Random(e) => StoreError::Encryption(e.to_string()), - store_key::Error::Serialization(e) => StoreError::Json(e), - store_key::Error::Encryption(e) => StoreError::Encryption(e), - }, - } - } -} pub struct IndexeddbStore { name: String, @@ -113,6 +136,8 @@ impl std::fmt::Debug for IndexeddbStore { } } +type Result = std::result::Result; + impl IndexeddbStore { async fn open_helper(name: String, store_key: Option) -> Result { // Open my_db v1 @@ -190,12 +215,12 @@ impl IndexeddbStore { if let DatabaseType::Encrypted(k) = key { StoreKey::import(passphrase, k).map_err(|_| StoreError::StoreLocked)? } else { - return Err(StoreError::UnencryptedStore); + return Err(StoreError::UnencryptedStore.into()); } } else { - let key = StoreKey::new().map_err::(|e| e.into())?; + let key = StoreKey::new().map_err::(|e| e.into())?; let encrypted_key = DatabaseType::Encrypted( - key.export(passphrase).map_err::(|e| e.into())?, + key.export(passphrase).map_err::(|e| e.into())?, ); ob.put_key_val( &JsValue::from_str(KEYS::STORE_KEY), @@ -213,7 +238,7 @@ impl IndexeddbStore { IndexeddbStore::open_helper(name, None).await } - fn serialize_event(&self, event: &impl Serialize) -> Result { + fn serialize_event(&self, event: &impl Serialize) -> std::result::Result { Ok(match self.store_key { Some(ref key) => JsValue::from_serde(&key.encrypt(event)?)?, None => JsValue::from_serde(event)?, @@ -223,7 +248,7 @@ impl IndexeddbStore { fn deserialize_event Deserialize<'b>>( &self, event: JsValue, - ) -> Result { + ) -> std::result::Result { match self.store_key { Some(ref key) => { let encrypted: EncryptedEvent = event.into_serde()?; @@ -468,7 +493,7 @@ impl IndexeddbStore { } } - tx.await.into_result().map_err::(|e| e.into()) + tx.await.into_result().map_err::(|e| e.into()) } pub async fn get_presence_event(&self, user_id: &UserId) -> Result>> { @@ -630,7 +655,7 @@ impl IndexeddbStore { .await? .map(|f| { self.deserialize_event::>>(f) - .map_err::(|e| e.into()) + .map_err::(|e| e.into()) }) .unwrap_or_else(|| Ok(Default::default())) } @@ -645,7 +670,7 @@ impl IndexeddbStore { .object_store(KEYS::ACCOUNT_DATA)? .get(&JsValue::from_str(event_type.as_str()))? .await? - .map(|f| self.deserialize_event(f).map_err::(|e| e.into())) + .map(|f| self.deserialize_event(f).map_err::(|e| e.into())) .transpose()?) } @@ -660,7 +685,7 @@ impl IndexeddbStore { .object_store(KEYS::ROOM_ACCOUNT_DATA)? .get(&(room_id.as_str(), event_type.as_str()).encode())? .await? - .map(|f| self.deserialize_event(f).map_err::(|e| e.into())) + .map(|f| self.deserialize_event(f).map_err::(|e| e.into())) .transpose()?) } @@ -704,7 +729,7 @@ impl IndexeddbStore { Box::::try_from(&k_str[prefix_len..]) .map_err(|e| StoreError::Codec(format!("{:?}", e)))? } else { - return Err(StoreError::Codec(format!("{:?}", k))); + return Err(StoreError::Codec(format!("{:?}", k)).into()); }; let r = self .deserialize_event::(res) @@ -766,7 +791,7 @@ impl IndexeddbStore { tx.object_store(KEYS::CUSTOM)?.put_key_val(&jskey, &self.serialize_event(&value)?)?; - tx.await.into_result().map_err::(|e| e.into())?; + tx.await.into_result().map_err::(|e| e.into())?; Ok(prev) } @@ -833,30 +858,30 @@ impl IndexeddbStore { store.delete(&key)?; } } - tx.await.into_result().map_err::(|e| e.into()) + tx.await.into_result().map_err::(|e| e.into()) } } #[async_trait(?Send)] impl StateStore for IndexeddbStore { - async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { - self.save_filter(filter_name, filter_id).await + async fn save_filter(&self, filter_name: &str, filter_id: &str) -> StoreResult<()> { + self.save_filter(filter_name, filter_id).await.map_err(|e| e.into()) } - async fn save_changes(&self, changes: &StateChanges) -> Result<()> { - self.save_changes(changes).await + async fn save_changes(&self, changes: &StateChanges) -> StoreResult<()> { + self.save_changes(changes).await.map_err(|e| e.into()) } - async fn get_filter(&self, filter_id: &str) -> Result> { - self.get_filter(filter_id).await + async fn get_filter(&self, filter_id: &str) -> StoreResult> { + self.get_filter(filter_id).await.map_err(|e| e.into()) } - async fn get_sync_token(&self) -> Result> { - self.get_sync_token().await + async fn get_sync_token(&self) -> StoreResult> { + self.get_sync_token().await.map_err(|e| e.into()) } - async fn get_presence_event(&self, user_id: &UserId) -> Result>> { - self.get_presence_event(user_id).await + async fn get_presence_event(&self, user_id: &UserId) -> StoreResult>> { + self.get_presence_event(user_id).await.map_err(|e| e.into()) } async fn get_state_event( @@ -864,75 +889,75 @@ impl StateStore for IndexeddbStore { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result>> { - self.get_state_event(room_id, event_type, state_key).await + ) -> StoreResult>> { + self.get_state_event(room_id, event_type, state_key).await.map_err(|e| e.into()) } async fn get_state_events( &self, room_id: &RoomId, event_type: EventType, - ) -> Result>> { - self.get_state_events(room_id, event_type).await + ) -> StoreResult>> { + self.get_state_events(room_id, event_type).await.map_err(|e| e.into()) } async fn get_profile( &self, room_id: &RoomId, user_id: &UserId, - ) -> Result> { - self.get_profile(room_id, user_id).await + ) -> StoreResult> { + self.get_profile(room_id, user_id).await.map_err(|e| e.into()) } async fn get_member_event( &self, room_id: &RoomId, state_key: &UserId, - ) -> Result> { - self.get_member_event(room_id, state_key).await + ) -> StoreResult> { + self.get_member_event(room_id, state_key).await.map_err(|e| e.into()) } - async fn get_user_ids(&self, room_id: &RoomId) -> Result>> { - self.get_user_ids_stream(room_id).await + async fn get_user_ids(&self, room_id: &RoomId) -> StoreResult>> { + self.get_user_ids_stream(room_id).await.map_err(|e| e.into()) } - async fn get_invited_user_ids(&self, room_id: &RoomId) -> Result>> { - self.get_invited_user_ids(room_id).await + async fn get_invited_user_ids(&self, room_id: &RoomId) -> StoreResult>> { + self.get_invited_user_ids(room_id).await.map_err(|e| e.into()) } - async fn get_joined_user_ids(&self, room_id: &RoomId) -> Result>> { - self.get_joined_user_ids(room_id).await + async fn get_joined_user_ids(&self, room_id: &RoomId) -> StoreResult>> { + self.get_joined_user_ids(room_id).await.map_err(|e| e.into()) } - async fn get_room_infos(&self) -> Result> { - self.get_room_infos().await + async fn get_room_infos(&self) -> StoreResult> { + self.get_room_infos().await.map_err(|e| e.into()) } - async fn get_stripped_room_infos(&self) -> Result> { - self.get_stripped_room_infos().await + async fn get_stripped_room_infos(&self) -> StoreResult> { + self.get_stripped_room_infos().await.map_err(|e| e.into()) } async fn get_users_with_display_name( &self, room_id: &RoomId, display_name: &str, - ) -> Result>> { - self.get_users_with_display_name(room_id, display_name).await + ) -> StoreResult>> { + self.get_users_with_display_name(room_id, display_name).await.map_err(|e| e.into()) } async fn get_account_data_event( &self, event_type: EventType, - ) -> Result>> { - self.get_account_data_event(event_type).await + ) -> StoreResult>> { + self.get_account_data_event(event_type).await.map_err(|e| e.into()) } async fn get_room_account_data_event( &self, room_id: &RoomId, event_type: EventType, - ) -> Result>> { - self.get_room_account_data_event(room_id, event_type).await + ) -> StoreResult>> { + self.get_room_account_data_event(room_id, event_type).await.map_err(|e| e.into()) } async fn get_user_room_receipt_event( @@ -940,8 +965,8 @@ impl StateStore for IndexeddbStore { room_id: &RoomId, receipt_type: ReceiptType, user_id: &UserId, - ) -> Result, Receipt)>> { - self.get_user_room_receipt_event(room_id, receipt_type, user_id).await + ) -> StoreResult, Receipt)>> { + self.get_user_room_receipt_event(room_id, receipt_type, user_id).await.map_err(|e| e.into()) } async fn get_event_room_receipt_events( @@ -949,36 +974,36 @@ impl StateStore for IndexeddbStore { room_id: &RoomId, receipt_type: ReceiptType, event_id: &EventId, - ) -> Result, Receipt)>> { - self.get_event_room_receipt_events(room_id, receipt_type, event_id).await + ) -> StoreResult, Receipt)>> { + self.get_event_room_receipt_events(room_id, receipt_type, event_id).await.map_err(|e| e.into()) } - async fn get_custom_value(&self, key: &[u8]) -> Result>> { - self.get_custom_value(key).await + async fn get_custom_value(&self, key: &[u8]) -> StoreResult>> { + self.get_custom_value(key).await.map_err(|e| e.into()) } - async fn set_custom_value(&self, key: &[u8], value: Vec) -> Result>> { - self.set_custom_value(key, value).await + async fn set_custom_value(&self, key: &[u8], value: Vec) -> StoreResult>> { + self.set_custom_value(key, value).await.map_err(|e| e.into()) } - async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { - self.add_media_content(request, data).await + async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> StoreResult<()> { + self.add_media_content(request, data).await.map_err(|e| e.into()) } - async fn get_media_content(&self, request: &MediaRequest) -> Result>> { - self.get_media_content(request).await + async fn get_media_content(&self, request: &MediaRequest) -> StoreResult>> { + self.get_media_content(request).await.map_err(|e| e.into()) } - async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { - self.remove_media_content(request).await + async fn remove_media_content(&self, request: &MediaRequest) -> StoreResult<()> { + self.remove_media_content(request).await.map_err(|e| e.into()) } - async fn remove_media_content_for_uri(&self, uri: &MxcUri) -> Result<()> { - self.remove_media_content_for_uri(uri).await + async fn remove_media_content_for_uri(&self, uri: &MxcUri) -> StoreResult<()> { + self.remove_media_content_for_uri(uri).await.map_err(|e| e.into()) } - async fn remove_room(&self, room_id: &RoomId) -> Result<()> { - self.remove_room(room_id).await + async fn remove_room(&self, room_id: &RoomId) -> StoreResult<()> { + self.remove_room(room_id).await.map_err(|e| e.into()) } }