ping-openmls-sdk-mls-store 0.6.13

Persistent OpenMLS provider — SQLite (native) / memory backends ([CR-4])
Documentation
//! Async single-blob backend for the WASM persistent-provider path.
//!
//! [CR-4] The Sqlite variant uses synchronous local-file I/O; the IndexedDb
//! variant CAN'T because IDB is async-only on the web. Rather than introduce
//! a Rust-side IDB binding (extra wasm-bindgen surface + duplicate the host's
//! AES-GCM wrapper), the WASM build asks the host to round-trip a single
//! serialized snapshot blob through its existing encrypted-IDB storage layer
//! (`packages/ui/src/storage-mls/PingStorage.web.ts`).
//!
//! Why a separate trait from `ping_core::Storage`: this crate must NOT depend
//! on `ping-core` (the dep edge runs the other way). The host implements
//! this trait by wrapping its own `Storage` — typically writing the
//! snapshot under a reserved namespace + key (e.g. `("__mls", "snapshot")`)
//! so it sits alongside the metadata entries the host already manages.

use std::future::Future;
use std::pin::Pin;

/// Future returned by [`AsyncBlobStore`] methods. Mirrors the
/// `Send`-bound pattern from `ping_core::storage::StorageFuture`: on native
/// targets the future must cross tokio threads, on WASM the runtime is
/// single-threaded and `JsFuture` is `!Send`.
#[cfg(not(target_arch = "wasm32"))]
pub type BlobFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[cfg(target_arch = "wasm32")]
pub type BlobFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

/// Async single-blob storage. Reads + writes the entire MLS snapshot as one
/// opaque byte slab; the provider takes care of (de)serialising the
/// `MemoryStorage` HashMap inside that slab.
///
/// Native targets get `Send + Sync` so the provider can be shared across
/// tokio threads; WASM drops the bound to match `wasm-bindgen` + `JsFuture`
/// being `!Send`.
///
/// `Debug` is required so [`crate::StorageBackend`] can still derive `Debug`
/// without resorting to a manual impl on the enum.
#[cfg(not(target_arch = "wasm32"))]
pub trait AsyncBlobStore: std::fmt::Debug + Send + Sync {
    /// Return the previously-written snapshot, or `Ok(None)` when the store
    /// is empty. Errors propagate as `String` so we don't have to pull the
    /// host's error type into this crate.
    fn read_blob(&self) -> BlobFuture<'_, std::result::Result<Option<Vec<u8>>, String>>;
    /// Overwrite the snapshot. Implementations MUST be atomic (a partial
    /// write would leave the provider unable to load on next cold start).
    fn write_blob(&self, bytes: Vec<u8>) -> BlobFuture<'_, std::result::Result<(), String>>;
}

#[cfg(target_arch = "wasm32")]
pub trait AsyncBlobStore: std::fmt::Debug {
    fn read_blob(&self) -> BlobFuture<'_, std::result::Result<Option<Vec<u8>>, String>>;
    fn write_blob(&self, bytes: Vec<u8>) -> BlobFuture<'_, std::result::Result<(), String>>;
}