diff --git a/patch-db/src/lib.rs b/patch-db/src/lib.rs index 0e4b0eb..0f69c2d 100644 --- a/patch-db/src/lib.rs +++ b/patch-db/src/lib.rs @@ -18,7 +18,7 @@ pub use imbl_value::Value; pub use model::{HasModel, Model, ModelExt}; pub use patch::{DiffPatch, Dump, Revision}; pub use patch_db_macro::HasModel; -pub use store::{PatchDb, Store, TypedPatchDb}; +pub use store::{MutateResult, PatchDb, Store, TypedPatchDb}; use tokio::sync::TryLockError; pub use {imbl_value as value, json_patch, json_ptr}; diff --git a/patch-db/src/store.rs b/patch-db/src/store.rs index 978b385..201bf56 100644 --- a/patch-db/src/store.rs +++ b/patch-db/src/store.rs @@ -256,6 +256,55 @@ impl Store { } } +#[must_use] +pub struct MutateResult { + pub result: Result, + pub revision: Option>, +} +impl MutateResult { + pub fn map_result) -> Result>( + self, + f: F, + ) -> MutateResult { + MutateResult { + result: f(self.result), + revision: self.revision, + } + } + pub fn map_ok T0>(self, f: F) -> MutateResult { + MutateResult { + result: self.result.map(f), + revision: self.revision, + } + } + pub fn map_err E0>(self, f: F) -> MutateResult { + MutateResult { + result: self.result.map_err(f), + revision: self.revision, + } + } + pub fn and_then Result>(self, f: F) -> MutateResult { + MutateResult { + result: self.result.and_then(f), + revision: self.revision, + } + } +} +impl From>), E>> for MutateResult { + fn from(value: Result<(T, Option>), E>) -> Self { + match value { + Ok((result, revision)) => Self { + result: Ok(result), + revision, + }, + Err(e) => Self { + result: Err(e), + revision: None, + }, + } + } +} + #[derive(Clone)] pub struct PatchDb { pub(crate) store: Arc>, @@ -311,23 +360,27 @@ impl PatchDb { let rev = store.apply(patch).await?; Ok(rev) } - pub async fn apply_function(&self, f: F) -> Result<(Value, T), E> + pub async fn apply_function(&self, f: F) -> MutateResult<(Value, T), E> where F: FnOnce(Value) -> Result<(Value, T), E> + UnwindSafe, E: From, { - let mut store = self.store.write().await; - let old = store.persistent.clone(); - let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| { - Error::Panic( - e.downcast() - .map(|a| *a) - .unwrap_or_else(|_| "UNKNOWN".to_owned()), - ) - })??; - let diff = diff(&store.persistent, &new); - store.apply(diff).await?; - Ok((new, res)) + async { + let mut store = self.store.write().await; + let old = store.persistent.clone(); + let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| { + Error::Panic( + e.downcast() + .map(|a| *a) + .unwrap_or_else(|_| "UNKNOWN".to_owned()), + ) + })??; + let diff = diff(&store.persistent, &new); + let rev = store.apply(diff).await?; + Ok(((new, res), rev)) + } + .await + .into() } pub async fn run_idempotent(&self, f: F) -> Result<(Value, T), E> where @@ -392,29 +445,24 @@ impl> TypedPatchDb { pub async fn mutate( &self, f: impl FnOnce(&mut T::Model) -> Result + UnwindSafe + Send, - ) -> Result { + ) -> MutateResult { use crate::ModelExt; - Ok(self - .apply_function(|mut v| { - let model = T::Model::value_as_mut(&mut v); - let res = f(model)?; - Ok::<_, E>((v, res)) - }) - .await? - .1) + self.apply_function(|mut v| { + let model = T::Model::value_as_mut(&mut v); + let res = f(model)?; + Ok::<_, E>((v, res)) + }) + .await + .map_ok(|(_, v)| v) } pub async fn map_mutate( &self, f: impl FnOnce(T::Model) -> Result + UnwindSafe + Send, - ) -> Result { + ) -> MutateResult { use crate::ModelExt; - Ok(T::Model::from_value( - self.apply_function(|v| { - f(T::Model::from_value(v)).map(|a| (T::Model::into_value(a), ())) - }) - .await? - .0, - )) + self.apply_function(|v| f(T::Model::from_value(v)).map(|a| (T::Model::into_value(a), ()))) + .await + .map_ok(|(v, _)| T::Model::from_value(v)) } } @@ -430,7 +478,8 @@ impl> TypedPatchDb Fut, Fut: Future>>(