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,11 +360,12 @@ 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>,
{ {
async {
let mut store = self.store.write().await; let mut store = self.store.write().await;
let old = store.persistent.clone(); let old = store.persistent.clone();
let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| { let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| {
@@ -326,8 +376,11 @@ impl PatchDb {
) )
})??; })??;
let diff = diff(&store.persistent, &new); let diff = diff(&store.persistent, &new);
store.apply(diff).await?; let rev = store.apply(diff).await?;
Ok((new, res)) 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
.1) .map_ok(|(_, v)| v)
} }
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>>>(