use std::future::Future;
use crate::{FaultInjector, FaultPointError, ScenarioBoundary, SimulatorFault};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CommitKnowledge {
NotExecuted,
RolledBack,
Committed,
Unknown,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DurableFailureCause<E> {
Backend(E),
Injected(SimulatorFault),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableFailure<E> {
pub knowledge: CommitKnowledge,
pub cause: DurableFailureCause<E>,
}
impl<E> DurableFailure<E> {
pub fn backend(knowledge: CommitKnowledge, error: E) -> Self {
Self {
knowledge,
cause: DurableFailureCause::Backend(error),
}
}
fn injected(knowledge: CommitKnowledge, fault: SimulatorFault) -> Self {
Self {
knowledge,
cause: DurableFailureCause::Injected(fault),
}
}
}
#[derive(Clone, Debug)]
pub struct DurableFaultFacade {
operation: String,
faults: FaultInjector,
}
impl DurableFaultFacade {
pub fn new(operation: &str, faults: FaultInjector) -> Result<Self, FaultPointError> {
for suffix in [
".before-operation",
".after-durable-commit",
".before-response",
] {
crate::faults::validate_point(&format!("{operation}{suffix}"))?;
}
crate::faults::validate_point(operation)?;
Ok(Self {
operation: operation.to_owned(),
faults,
})
}
pub async fn execute<T, E, F, Fut>(&self, delegate: F) -> Result<T, DurableFailure<E>>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<T, DurableFailure<E>>>,
{
if let Err(fault) = self.check(ScenarioBoundary::BeforeOperation) {
return Err(DurableFailure::injected(
CommitKnowledge::NotExecuted,
fault,
));
}
let result = delegate().await;
if result.is_ok()
&& let Err(fault) = self.check(ScenarioBoundary::AfterDurableCommit)
{
return Err(DurableFailure::injected(CommitKnowledge::Committed, fault));
}
if let Err(fault) = self.check(ScenarioBoundary::BeforeResponse) {
return Err(DurableFailure::injected(CommitKnowledge::Unknown, fault));
}
result
}
fn check(&self, boundary: ScenarioBoundary) -> Result<(), SimulatorFault> {
self.faults
.check_at(&self.operation, boundary)
.expect("facade constructor validated every boundary")
}
}