diff --git a/Cargo.lock b/Cargo.lock index 9186c972a1c..5d7cb21f826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2117,8 +2117,10 @@ dependencies = [ "serde", "serde-wasm-bindgen", "sqlx", + "thiserror", "tokio", "wasm-bindgen-test", + "web-sys", ] [[package]] diff --git a/libparsec/crates/platform_storage/Cargo.toml b/libparsec/crates/platform_storage/Cargo.toml index f78968374eb..31daa3959d8 100644 --- a/libparsec/crates/platform_storage/Cargo.toml +++ b/libparsec/crates/platform_storage/Cargo.toml @@ -24,6 +24,7 @@ libparsec_testbed = { workspace = true, optional = true } log = { workspace = true } paste = { workspace = true } lazy_static = { workspace = true, optional = true } +thiserror = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true, features = ["fs", "sync"] } @@ -32,8 +33,9 @@ libsqlite3-sys = { workspace = true, features = ["bundled"] } sqlx = { workspace = true, features = ["sqlite", "runtime-tokio", "macros"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -indexed_db_futures = { workspace = true, features = ["indices"] } +indexed_db_futures = { workspace = true, features = ["indices", "cursors"] } js-sys = { workspace = true } +web-sys = { workspace = true, features = ["IdbKeyRange"] } serde = { workspace = true } serde-wasm-bindgen = { workspace = true } diff --git a/libparsec/crates/platform_storage/src/native/workspace.rs b/libparsec/crates/platform_storage/src/native/workspace.rs index 44650118dc1..019f463e04f 100644 --- a/libparsec/crates/platform_storage/src/native/workspace.rs +++ b/libparsec/crates/platform_storage/src/native/workspace.rs @@ -205,7 +205,7 @@ impl PlatformWorkspaceStorage { page: u32, limit: u32, ) -> anyhow::Result> { - db_list_manifest(&mut self.conn, page, limit).await + db_list_manifests(&mut self.conn, page, limit).await } pub async fn get_chunk(&mut self, chunk_id: ChunkID) -> anyhow::Result>> { @@ -650,7 +650,7 @@ async fn db_get_manifest( } } -async fn db_list_manifest( +async fn db_list_manifests( executor: impl sqlx::Executor<'_, Database = sqlx::Sqlite>, page: u32, limit: u32, diff --git a/libparsec/crates/platform_storage/src/web/db.rs b/libparsec/crates/platform_storage/src/web/db.rs index fa4a549ec61..4525211985d 100644 --- a/libparsec/crates/platform_storage/src/web/db.rs +++ b/libparsec/crates/platform_storage/src/web/db.rs @@ -93,6 +93,59 @@ where .map_err(|e| anyhow::anyhow!("{e:?}")) } +pub(super) async fn list( + tx: &IdbTransaction<'_>, + store: &str, + page: u32, + limit: u32, +) -> anyhow::Result> +where + V: DeserializeOwned, +{ + use js_sys::Number; + use web_sys::IdbKeyRange; + + // Index start at 1 + let start = page * limit + 1; + let end = start + limit; + + let range = IdbKeyRange::bound_with_lower_open_and_upper_open( + &Number::from(start), + &Number::from(end), + false, + true, + ) + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + + let store = tx + .object_store(store) + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + + let Some(cursor) = store + .open_cursor_with_range_owned(range) + .map_err(|e| anyhow::anyhow!("{e:?}"))? + .await + .map_err(|e| anyhow::anyhow!("{e:?}"))? + else { + return Ok(Vec::new()); + }; + + cursor + .into_vec(0) + .await + .map_err(|e| anyhow::anyhow!("{e:?}")) + .and_then(|v: Vec<_>| { + v.into_iter() + .map(|key_val| + // TODO: Sad that KeyVal does not provide it's internal value without a reference. + // That reference force us to clone the value, which is not optimal. + // Could be improved if https://github.com/Alorel/rust-indexed-db/issues/39 is fixed + serde_wasm_bindgen::from_value(key_val.value().clone()) + .map_err(|e| anyhow::anyhow!("{e:?}"))) + .collect::>>() + }) +} + pub(super) async fn count( tx: &IdbTransaction<'_>, store: &str, diff --git a/libparsec/crates/platform_storage/src/web/model.rs b/libparsec/crates/platform_storage/src/web/model.rs index 9a6d14a7913..732a298778d 100644 --- a/libparsec/crates/platform_storage/src/web/model.rs +++ b/libparsec/crates/platform_storage/src/web/model.rs @@ -574,6 +574,15 @@ impl Vlob { super::db::get_all(&tx, Self::STORE).await } + pub(super) async fn list( + conn: &IdbDatabase, + page: u32, + limit: u32, + ) -> anyhow::Result> { + let tx = Self::read(conn)?; + super::db::list(&tx, Self::STORE, page, limit).await + } + pub(super) async fn remove(tx: &IdbTransaction<'_>, vlob_id: &Bytes) -> anyhow::Result<()> { let vlob_id = serde_wasm_bindgen::to_value(vlob_id).map_err(|e| anyhow::anyhow!("{e:?}"))?; diff --git a/libparsec/crates/platform_storage/src/web/workspace.rs b/libparsec/crates/platform_storage/src/web/workspace.rs index 31b483cd0a1..04e7dbdb976 100644 --- a/libparsec/crates/platform_storage/src/web/workspace.rs +++ b/libparsec/crates/platform_storage/src/web/workspace.rs @@ -16,7 +16,7 @@ use crate::{ model::{RealmCheckpoint, Vlob}, DB_VERSION, }, - workspace::{PopulateManifestOutcome, UpdateManifestData}, + workspace::{PopulateManifestOutcome, RawEncryptedManifest, UpdateManifestData}, }; use super::{db, model::PreventSyncPattern}; @@ -130,7 +130,10 @@ impl PlatformWorkspaceStorage { .collect::, _>>() } - pub async fn get_manifest(&mut self, entry_id: VlobID) -> anyhow::Result>> { + pub async fn get_manifest( + &mut self, + entry_id: VlobID, + ) -> anyhow::Result> { let transaction = Vlob::read(&self.conn)?; Ok( @@ -140,6 +143,16 @@ impl PlatformWorkspaceStorage { ) } + pub async fn list_manifests( + &mut self, + page: u32, + limit: u32, + ) -> anyhow::Result> { + Vlob::list(&self.conn, page, limit) + .await + .map(|vlobs| vlobs.into_iter().map(|vlob| vlob.blob.to_vec()).collect()) + } + pub async fn get_chunk(&mut self, chunk_id: ChunkID) -> anyhow::Result>> { let transaction = super::model::Chunk::read(&self.conn)?; db_get_chunk(&transaction, chunk_id).await