use crate::error::GraphError;
use crate::graph::change::{ChangeSet, CommitMetadata};
use crate::graph::hooks::RollbackReason;
use crate::graph::storage::{GraphStorage, UNWOUND, close_on_unwind};
const CLOSURE_FAILED: &str = "the transaction closure returned an error";
pub trait Transactional: GraphStorage {
fn transaction<T, E>(&mut self, body: impl FnOnce(&mut Self) -> Result<T, E>) -> Result<T, E>
where
E: From<GraphError>,
{
self.transaction_with(CommitMetadata::default(), body)
}
fn transaction_with<T, E>(
&mut self,
metadata: CommitMetadata,
body: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<T, E>
where
E: From<GraphError>,
{
let mark = self.begin_unit();
let outcome = close_on_unwind(self, body, |this| {
this.rollback_unit(mark, &RollbackReason::failed(&UNWOUND));
});
match outcome {
Ok(value) => {
self.set_unit_metadata(metadata);
self.commit_unit(mark)?;
Ok(value)
}
Err(err) => {
self.rollback_unit(mark, &RollbackReason::failed(&CLOSURE_FAILED));
Err(err)
}
}
}
fn dry_run<T, E>(
&mut self,
body: impl FnOnce(&mut Self) -> Result<T, E>,
) -> Result<(T, ChangeSet), E>
where
E: From<GraphError>,
{
let mark = self.begin_unit();
let outcome = close_on_unwind(self, body, |this| {
let _ = this.discard_unit(mark);
});
match outcome {
Ok(value) => {
let changes = self.discard_unit(mark)?;
debug_assert!(
self.capabilities().change_capture,
"a storage that overrides discard_unit must claim change_capture"
);
Ok((value, changes))
}
Err(err) => {
let _ = self.discard_unit(mark);
Err(err)
}
}
}
}
impl<G: GraphStorage + ?Sized> Transactional for G {}