use mls_rs::error::IntoAnyError; use std::fmt::Debug; #[cfg(not(mls_build_async))] use std::sync::Mutex; #[cfg(mls_build_async)] use tokio::sync::Mutex; use crate::Error; // TODO(mulmarta): we'd like to use EpochRecord from mls-rs-core but // this breaks the Python tests because using two crates makes UniFFI // generate a Python module which must be in a subdirectory of the // directory with test scripts which is not supported by the script we // use. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, uniffi::Record)] pub struct EpochRecord { /// A unique epoch identifier within a particular group. pub id: u64, pub data: Vec, } impl From for EpochRecord { fn from(mls_rs_core::group::EpochRecord { id, data }: mls_rs_core::group::EpochRecord) -> Self { Self { id, data } } } impl From for mls_rs_core::group::EpochRecord { fn from(EpochRecord { id, data }: EpochRecord) -> Self { Self { id, data } } } // When building for async, uniffi::export has to be applied _after_ // maybe-async's injection of the async trait. When building for sync, // the order has to be the opposite. #[cfg_attr(mls_build_async, uniffi::export(with_foreign))] #[cfg_attr(mls_build_async, maybe_async::must_be_async)] #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] #[cfg_attr(not(mls_build_async), uniffi::export(with_foreign))] pub trait GroupStateStorage: Send + Sync + Debug { async fn state(&self, group_id: Vec) -> Result>, Error>; async fn epoch(&self, group_id: Vec, epoch_id: u64) -> Result>, Error>; async fn write( &self, group_id: Vec, group_state: Vec, epoch_inserts: Vec, epoch_updates: Vec, ) -> Result<(), Error>; async fn max_epoch_id(&self, group_id: Vec) -> Result, Error>; } /// Adapt a mls-rs `GroupStateStorage` implementation. /// /// This is used to adapt a mls-rs `GroupStateStorage` implementation /// to our own `GroupStateStorage` trait. This way we can use any /// standard mls-rs group state storage from the FFI layer. #[derive(Debug)] pub(crate) struct GroupStateStorageAdapter(Mutex); impl GroupStateStorageAdapter { pub fn new(group_state_storage: S) -> GroupStateStorageAdapter { Self(Mutex::new(group_state_storage)) } #[cfg(not(mls_build_async))] fn inner(&self) -> std::sync::MutexGuard<'_, S> { self.0.lock().unwrap() } #[cfg(mls_build_async)] async fn inner(&self) -> tokio::sync::MutexGuard<'_, S> { self.0.lock().await } } #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] #[cfg_attr(mls_build_async, maybe_async::must_be_async)] impl GroupStateStorage for GroupStateStorageAdapter where S: mls_rs::GroupStateStorage + Debug, Err: IntoAnyError, { async fn state(&self, group_id: Vec) -> Result>, Error> { self.inner() .await .state(&group_id) .await .map_err(|err| err.into_any_error().into()) } async fn epoch(&self, group_id: Vec, epoch_id: u64) -> Result>, Error> { self.inner() .await .epoch(&group_id, epoch_id) .await .map_err(|err| err.into_any_error().into()) } async fn write( &self, id: Vec, data: Vec, epoch_inserts: Vec, epoch_updates: Vec, ) -> Result<(), Error> { self.inner() .await .write( mls_rs_core::group::GroupState { id, data }, epoch_inserts.into_iter().map(Into::into).collect(), epoch_updates.into_iter().map(Into::into).collect(), ) .await .map_err(|err| err.into_any_error().into()) } async fn max_epoch_id(&self, group_id: Vec) -> Result, Error> { self.inner() .await .max_epoch_id(&group_id) .await .map_err(|err| err.into_any_error().into()) } }