use tsoracle_consensus::ConsensusError;
use tsoracle_core::Epoch;
#[derive(Debug)]
pub(crate) enum PersistDisposition {
SteppedDown { fenced_by: Option<Epoch> },
Transient(Box<dyn std::error::Error + Send + Sync>),
Permanent(Box<dyn std::error::Error + Send + Sync>),
}
pub(crate) fn classify(error: ConsensusError) -> PersistDisposition {
match error {
ConsensusError::Fenced { current, .. } => PersistDisposition::SteppedDown {
fenced_by: Some(current),
},
ConsensusError::NotLeader { .. } => PersistDisposition::SteppedDown { fenced_by: None },
ConsensusError::TransientDriver(source) => PersistDisposition::Transient(source),
ConsensusError::PermanentDriver(source) => PersistDisposition::Permanent(source),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fenced_carries_the_current_epoch_into_the_hint() {
let disposition = classify(ConsensusError::Fenced {
expected: Epoch(1),
current: Epoch(2),
});
assert!(matches!(
disposition,
PersistDisposition::SteppedDown {
fenced_by: Some(Epoch(2))
}
));
}
#[test]
fn not_leader_steps_down_without_an_epoch() {
let disposition = classify(ConsensusError::NotLeader {
observed: Some(Epoch(7)),
});
assert!(matches!(
disposition,
PersistDisposition::SteppedDown { fenced_by: None }
));
}
#[test]
fn transient_preserves_the_source_text() {
let disposition = classify(ConsensusError::TransientDriver(Box::new(
std::io::Error::other("flap"),
)));
match disposition {
PersistDisposition::Transient(source) => assert_eq!(source.to_string(), "flap"),
other => panic!("expected Transient, got a different disposition: {other:?}"),
}
}
#[test]
fn permanent_preserves_the_source_text() {
let disposition = classify(ConsensusError::PermanentDriver(Box::new(
std::io::Error::other("corrupted"),
)));
match disposition {
PersistDisposition::Permanent(source) => assert_eq!(source.to_string(), "corrupted"),
other => panic!("expected Permanent, got a different disposition: {other:?}"),
}
}
}