-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[uniffi] Add support for custom GroupStateStorage interface (#86)
* [uniffi] Add support for custom GroupStateStorage interface * Allow external languages to implement callbacks * Remove unwrap from group_state.rs * Fix simple_scenario_sync and remove async for now * Ignore async tests
- Loading branch information
Showing
6 changed files
with
289 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use std::{fmt::Debug, sync::Arc}; | ||
|
||
use mls_rs::{ | ||
client_builder::{self, WithGroupStateStorage}, | ||
identity::basic, | ||
}; | ||
use mls_rs_core::error::IntoAnyError; | ||
use mls_rs_crypto_openssl::OpensslCryptoProvider; | ||
|
||
use self::group_state::GroupStateStorageWrapper; | ||
|
||
mod group_state; | ||
|
||
#[derive(Debug, thiserror::Error, uniffi::Error)] | ||
#[uniffi(flat_error)] | ||
#[non_exhaustive] | ||
pub enum FFICallbackError { | ||
#[error("data preparation error")] | ||
DataPreparationError { | ||
#[from] | ||
inner: mls_rs_core::mls_rs_codec::Error, | ||
}, | ||
#[error("unexpected callback error")] | ||
UnexpectedCallbackError { | ||
#[from] | ||
inner: uniffi::UnexpectedUniFFICallbackError, | ||
}, | ||
} | ||
|
||
impl IntoAnyError for FFICallbackError {} | ||
|
||
pub type UniFFIConfig = client_builder::WithIdentityProvider< | ||
basic::BasicIdentityProvider, | ||
client_builder::WithCryptoProvider< | ||
OpensslCryptoProvider, | ||
WithGroupStateStorage<GroupStateStorageWrapper, client_builder::BaseConfig>, | ||
>, | ||
>; | ||
|
||
#[derive(Debug, Clone, uniffi::Record)] | ||
pub struct ClientConfig { | ||
pub group_state_storage: Arc<dyn group_state::GroupStateStorage>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use std::{fmt::Debug, sync::Arc}; | ||
|
||
use mls_rs_core::mls_rs_codec::{MlsDecode, MlsEncode}; | ||
|
||
use super::FFICallbackError; | ||
|
||
#[derive(Clone, Debug, uniffi::Record)] | ||
pub struct GroupState { | ||
pub id: Vec<u8>, | ||
pub data: Vec<u8>, | ||
} | ||
|
||
impl mls_rs_core::group::GroupState for GroupState { | ||
fn id(&self) -> Vec<u8> { | ||
self.id.clone() | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, uniffi::Record)] | ||
pub struct EpochRecord { | ||
pub id: u64, | ||
pub data: Vec<u8>, | ||
} | ||
|
||
impl mls_rs_core::group::EpochRecord for EpochRecord { | ||
fn id(&self) -> u64 { | ||
self.id | ||
} | ||
} | ||
|
||
#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] | ||
#[cfg_attr(mls_build_async, maybe_async::must_be_async)] | ||
#[uniffi::export(with_foreign)] | ||
pub trait GroupStateStorage: Send + Sync + Debug { | ||
async fn state(&self, group_id: Vec<u8>) -> Result<Option<Vec<u8>>, FFICallbackError>; | ||
async fn epoch( | ||
&self, | ||
group_id: Vec<u8>, | ||
epoch_id: u64, | ||
) -> Result<Option<Vec<u8>>, FFICallbackError>; | ||
|
||
async fn write( | ||
&self, | ||
state: GroupState, | ||
epoch_inserts: Vec<EpochRecord>, | ||
epoch_updates: Vec<EpochRecord>, | ||
) -> Result<(), FFICallbackError>; | ||
|
||
async fn max_epoch_id(&self, group_id: Vec<u8>) -> Result<Option<u64>, FFICallbackError>; | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub(crate) struct GroupStateStorageWrapper(Arc<dyn GroupStateStorage>); | ||
|
||
impl From<Arc<dyn GroupStateStorage>> for GroupStateStorageWrapper { | ||
fn from(value: Arc<dyn GroupStateStorage>) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] | ||
#[cfg_attr(mls_build_async, maybe_async::must_be_async)] | ||
impl mls_rs_core::group::GroupStateStorage for GroupStateStorageWrapper { | ||
type Error = FFICallbackError; | ||
|
||
async fn state<T>(&self, group_id: &[u8]) -> Result<Option<T>, Self::Error> | ||
where | ||
T: mls_rs_core::group::GroupState + MlsEncode + MlsDecode, | ||
{ | ||
let state_data = self.0.state(group_id.to_vec())?; | ||
|
||
state_data | ||
.as_deref() | ||
.map(|v| T::mls_decode(&mut &*v)) | ||
.transpose() | ||
.map_err(Into::into) | ||
} | ||
|
||
async fn epoch<T>(&self, group_id: &[u8], epoch_id: u64) -> Result<Option<T>, Self::Error> | ||
where | ||
T: mls_rs_core::group::EpochRecord + MlsEncode + MlsDecode, | ||
{ | ||
let epoch_data = self.0.epoch(group_id.to_vec(), epoch_id)?; | ||
|
||
epoch_data | ||
.as_deref() | ||
.map(|v| T::mls_decode(&mut &*v)) | ||
.transpose() | ||
.map_err(Into::into) | ||
} | ||
|
||
async fn write<ST, ET>( | ||
&mut self, | ||
state: ST, | ||
epoch_inserts: Vec<ET>, | ||
epoch_updates: Vec<ET>, | ||
) -> Result<(), Self::Error> | ||
where | ||
ST: mls_rs_core::group::GroupState + MlsEncode + MlsDecode + Send + Sync, | ||
ET: mls_rs_core::group::EpochRecord + MlsEncode + MlsDecode + Send + Sync, | ||
{ | ||
let state = GroupState { | ||
id: state.id(), | ||
data: state.mls_encode_to_vec()?, | ||
}; | ||
|
||
let epoch_to_record = |v: ET| -> Result<_, Self::Error> { | ||
Ok(EpochRecord { | ||
id: v.id(), | ||
data: v.mls_encode_to_vec()?, | ||
}) | ||
}; | ||
|
||
let inserts = epoch_inserts | ||
.into_iter() | ||
.map(epoch_to_record) | ||
.collect::<Result<Vec<_>, _>>()?; | ||
|
||
let updates = epoch_updates | ||
.into_iter() | ||
.map(epoch_to_record) | ||
.collect::<Result<Vec<_>, _>>()?; | ||
|
||
self.0.write(state, inserts, updates) | ||
} | ||
|
||
async fn max_epoch_id(&self, group_id: &[u8]) -> Result<Option<u64>, Self::Error> { | ||
self.0.max_epoch_id(group_id.to_vec()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.