lift result from mutate

This commit is contained in:
Aiden McClelland
2025-03-20 12:33:44 -06:00
parent 0df18c651f
commit d1362bdcd9
2 changed files with 81 additions and 32 deletions

View File

@@ -18,7 +18,7 @@ pub use imbl_value::Value;
pub use model::{HasModel, Model, ModelExt}; pub use model::{HasModel, Model, ModelExt};
pub use patch::{DiffPatch, Dump, Revision}; pub use patch::{DiffPatch, Dump, Revision};
pub use patch_db_macro::HasModel; pub use patch_db_macro::HasModel;
pub use store::{PatchDb, Store, TypedPatchDb}; pub use store::{MutateResult, PatchDb, Store, TypedPatchDb};
use tokio::sync::TryLockError; use tokio::sync::TryLockError;
pub use {imbl_value as value, json_patch, json_ptr}; pub use {imbl_value as value, json_patch, json_ptr};

View File

@@ -256,6 +256,55 @@ impl Store {
} }
} }
#[must_use]
pub struct MutateResult<T, E> {
pub result: Result<T, E>,
pub revision: Option<Arc<Revision>>,
}
impl<T, E> MutateResult<T, E> {
pub fn map_result<T0, E0, F: FnOnce(Result<T, E>) -> Result<T0, E0>>(
self,
f: F,
) -> MutateResult<T0, E0> {
MutateResult {
result: f(self.result),
revision: self.revision,
}
}
pub fn map_ok<T0, F: FnOnce(T) -> T0>(self, f: F) -> MutateResult<T0, E> {
MutateResult {
result: self.result.map(f),
revision: self.revision,
}
}
pub fn map_err<E0, F: FnOnce(E) -> E0>(self, f: F) -> MutateResult<T, E0> {
MutateResult {
result: self.result.map_err(f),
revision: self.revision,
}
}
pub fn and_then<T0, F: FnOnce(T) -> Result<T0, E>>(self, f: F) -> MutateResult<T0, E> {
MutateResult {
result: self.result.and_then(f),
revision: self.revision,
}
}
}
impl<T, E> From<Result<(T, Option<Arc<Revision>>), E>> for MutateResult<T, E> {
fn from(value: Result<(T, Option<Arc<Revision>>), E>) -> Self {
match value {
Ok((result, revision)) => Self {
result: Ok(result),
revision,
},
Err(e) => Self {
result: Err(e),
revision: None,
},
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct PatchDb { pub struct PatchDb {
pub(crate) store: Arc<RwLock<Store>>, pub(crate) store: Arc<RwLock<Store>>,
@@ -311,23 +360,27 @@ impl PatchDb {
let rev = store.apply(patch).await?; let rev = store.apply(patch).await?;
Ok(rev) Ok(rev)
} }
pub async fn apply_function<F, T, E>(&self, f: F) -> Result<(Value, T), E> pub async fn apply_function<F, T, E>(&self, f: F) -> MutateResult<(Value, T), E>
where where
F: FnOnce(Value) -> Result<(Value, T), E> + UnwindSafe, F: FnOnce(Value) -> Result<(Value, T), E> + UnwindSafe,
E: From<Error>, E: From<Error>,
{ {
let mut store = self.store.write().await; async {
let old = store.persistent.clone(); let mut store = self.store.write().await;
let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| { let old = store.persistent.clone();
Error::Panic( let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| {
e.downcast() Error::Panic(
.map(|a| *a) e.downcast()
.unwrap_or_else(|_| "UNKNOWN".to_owned()), .map(|a| *a)
) .unwrap_or_else(|_| "UNKNOWN".to_owned()),
})??; )
let diff = diff(&store.persistent, &new); })??;
store.apply(diff).await?; let diff = diff(&store.persistent, &new);
Ok((new, res)) let rev = store.apply(diff).await?;
Ok(((new, res), rev))
}
.await
.into()
} }
pub async fn run_idempotent<F, Fut, T, E>(&self, f: F) -> Result<(Value, T), E> pub async fn run_idempotent<F, Fut, T, E>(&self, f: F) -> Result<(Value, T), E>
where where
@@ -392,29 +445,24 @@ impl<T: HasModel, E: From<Error>> TypedPatchDb<T, E> {
pub async fn mutate<U: UnwindSafe + Send>( pub async fn mutate<U: UnwindSafe + Send>(
&self, &self,
f: impl FnOnce(&mut T::Model) -> Result<U, E> + UnwindSafe + Send, f: impl FnOnce(&mut T::Model) -> Result<U, E> + UnwindSafe + Send,
) -> Result<U, E> { ) -> MutateResult<U, E> {
use crate::ModelExt; use crate::ModelExt;
Ok(self self.apply_function(|mut v| {
.apply_function(|mut v| { let model = T::Model::value_as_mut(&mut v);
let model = T::Model::value_as_mut(&mut v); let res = f(model)?;
let res = f(model)?; Ok::<_, E>((v, res))
Ok::<_, E>((v, res)) })
}) .await
.await? .map_ok(|(_, v)| v)
.1)
} }
pub async fn map_mutate( pub async fn map_mutate(
&self, &self,
f: impl FnOnce(T::Model) -> Result<T::Model, E> + UnwindSafe + Send, f: impl FnOnce(T::Model) -> Result<T::Model, E> + UnwindSafe + Send,
) -> Result<T::Model, E> { ) -> MutateResult<T::Model, E> {
use crate::ModelExt; 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), ())))
self.apply_function(|v| { .await
f(T::Model::from_value(v)).map(|a| (T::Model::into_value(a), ())) .map_ok(|(v, _)| T::Model::from_value(v))
})
.await?
.0,
))
} }
} }
@@ -430,7 +478,8 @@ impl<T: HasModel + DeserializeOwned + Serialize, E: From<Error>> TypedPatchDb<T,
.map_err(Error::from)?, .map_err(Error::from)?,
)) ))
}) })
.await?; .await
.result?;
Ok(res) Ok(res)
} }
pub async fn load_or_init<F: FnOnce() -> Fut, Fut: Future<Output = Result<T, E>>>( pub async fn load_or_init<F: FnOnce() -> Fut, Fut: Future<Output = Result<T, E>>>(