1 use mls_rs::error::IntoAnyError; 2 use std::fmt::Debug; 3 #[cfg(not(mls_build_async))] 4 use std::sync::Mutex; 5 #[cfg(mls_build_async)] 6 use tokio::sync::Mutex; 7 8 use crate::Error; 9 10 // TODO(mulmarta): we'd like to use EpochRecord from mls-rs-core but 11 // this breaks the Python tests because using two crates makes UniFFI 12 // generate a Python module which must be in a subdirectory of the 13 // directory with test scripts which is not supported by the script we 14 // use. 15 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, uniffi::Record)] 16 pub struct EpochRecord { 17 /// A unique epoch identifier within a particular group. 18 pub id: u64, 19 pub data: Vec<u8>, 20 } 21 22 impl From<mls_rs_core::group::EpochRecord> for EpochRecord { 23 fn from(mls_rs_core::group::EpochRecord { id, data }: mls_rs_core::group::EpochRecord) -> Self { 24 Self { id, data } 25 } 26 } 27 28 impl From<EpochRecord> for mls_rs_core::group::EpochRecord { 29 fn from(EpochRecord { id, data }: EpochRecord) -> Self { 30 Self { id, data } 31 } 32 } 33 34 // When building for async, uniffi::export has to be applied _after_ 35 // maybe-async's injection of the async trait. When building for sync, 36 // the order has to be the opposite. 37 #[cfg_attr(mls_build_async, uniffi::export(with_foreign))] 38 #[cfg_attr(mls_build_async, maybe_async::must_be_async)] 39 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] 40 #[cfg_attr(not(mls_build_async), uniffi::export(with_foreign))] 41 pub trait GroupStateStorage: Send + Sync + Debug { state(&self, group_id: Vec<u8>) -> Result<Option<Vec<u8>>, Error>42 async fn state(&self, group_id: Vec<u8>) -> Result<Option<Vec<u8>>, Error>; epoch(&self, group_id: Vec<u8>, epoch_id: u64) -> Result<Option<Vec<u8>>, Error>43 async fn epoch(&self, group_id: Vec<u8>, epoch_id: u64) -> Result<Option<Vec<u8>>, Error>; 44 write( &self, group_id: Vec<u8>, group_state: Vec<u8>, epoch_inserts: Vec<EpochRecord>, epoch_updates: Vec<EpochRecord>, ) -> Result<(), Error>45 async fn write( 46 &self, 47 group_id: Vec<u8>, 48 group_state: Vec<u8>, 49 epoch_inserts: Vec<EpochRecord>, 50 epoch_updates: Vec<EpochRecord>, 51 ) -> Result<(), Error>; 52 max_epoch_id(&self, group_id: Vec<u8>) -> Result<Option<u64>, Error>53 async fn max_epoch_id(&self, group_id: Vec<u8>) -> Result<Option<u64>, Error>; 54 } 55 56 /// Adapt a mls-rs `GroupStateStorage` implementation. 57 /// 58 /// This is used to adapt a mls-rs `GroupStateStorage` implementation 59 /// to our own `GroupStateStorage` trait. This way we can use any 60 /// standard mls-rs group state storage from the FFI layer. 61 #[derive(Debug)] 62 pub(crate) struct GroupStateStorageAdapter<S>(Mutex<S>); 63 64 impl<S> GroupStateStorageAdapter<S> { new(group_state_storage: S) -> GroupStateStorageAdapter<S>65 pub fn new(group_state_storage: S) -> GroupStateStorageAdapter<S> { 66 Self(Mutex::new(group_state_storage)) 67 } 68 69 #[cfg(not(mls_build_async))] inner(&self) -> std::sync::MutexGuard<'_, S>70 fn inner(&self) -> std::sync::MutexGuard<'_, S> { 71 self.0.lock().unwrap() 72 } 73 74 #[cfg(mls_build_async)] inner(&self) -> tokio::sync::MutexGuard<'_, S>75 async fn inner(&self) -> tokio::sync::MutexGuard<'_, S> { 76 self.0.lock().await 77 } 78 } 79 80 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] 81 #[cfg_attr(mls_build_async, maybe_async::must_be_async)] 82 impl<S, Err> GroupStateStorage for GroupStateStorageAdapter<S> 83 where 84 S: mls_rs::GroupStateStorage<Error = Err> + Debug, 85 Err: IntoAnyError, 86 { state(&self, group_id: Vec<u8>) -> Result<Option<Vec<u8>>, Error>87 async fn state(&self, group_id: Vec<u8>) -> Result<Option<Vec<u8>>, Error> { 88 self.inner() 89 .await 90 .state(&group_id) 91 .await 92 .map_err(|err| err.into_any_error().into()) 93 } 94 epoch(&self, group_id: Vec<u8>, epoch_id: u64) -> Result<Option<Vec<u8>>, Error>95 async fn epoch(&self, group_id: Vec<u8>, epoch_id: u64) -> Result<Option<Vec<u8>>, Error> { 96 self.inner() 97 .await 98 .epoch(&group_id, epoch_id) 99 .await 100 .map_err(|err| err.into_any_error().into()) 101 } 102 write( &self, id: Vec<u8>, data: Vec<u8>, epoch_inserts: Vec<EpochRecord>, epoch_updates: Vec<EpochRecord>, ) -> Result<(), Error>103 async fn write( 104 &self, 105 id: Vec<u8>, 106 data: Vec<u8>, 107 epoch_inserts: Vec<EpochRecord>, 108 epoch_updates: Vec<EpochRecord>, 109 ) -> Result<(), Error> { 110 self.inner() 111 .await 112 .write( 113 mls_rs_core::group::GroupState { id, data }, 114 epoch_inserts.into_iter().map(Into::into).collect(), 115 epoch_updates.into_iter().map(Into::into).collect(), 116 ) 117 .await 118 .map_err(|err| err.into_any_error().into()) 119 } 120 max_epoch_id(&self, group_id: Vec<u8>) -> Result<Option<u64>, Error>121 async fn max_epoch_id(&self, group_id: Vec<u8>) -> Result<Option<u64>, Error> { 122 self.inner() 123 .await 124 .max_epoch_id(&group_id) 125 .await 126 .map_err(|err| err.into_any_error().into()) 127 } 128 } 129