use std::convert::Infallible;
use std::sync::Arc;
use thiserror::Error;
use crate::append::AppendError;
use crate::invocation::AbstainOf;
use crate::invocation::CommunicationErrorOf;
use crate::invocation::CoordNumOf;
use crate::invocation::Invocation;
use crate::invocation::LogEntryIdOf;
use crate::invocation::LogEntryOf;
use crate::invocation::NayOf;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum SpawnError<E> {
#[error("executor raised an error")]
ExecutorError(E),
#[error("a node decoration raised an error")]
Decoration(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
}
#[derive(Debug, Error)]
pub enum AffirmSnapshotError {
#[error("unknown snapshot")]
Unknown,
#[error("node is shut down")]
ShutDown,
}
#[derive(Debug, Error)]
pub enum InstallSnapshotError {
#[error("snapshot is outdated")]
Outdated,
#[error("node is shut down")]
ShutDown,
}
#[derive(Debug, Error)]
pub enum ReadStaleError {
#[error("node is disoriented")]
Disoriented,
#[error("node is shut down")]
ShutDown,
}
#[derive(Error)]
pub enum PrepareError<I: Invocation> {
#[error("promise war deliberately withheld")]
Abstained(AbstainOf<I>),
#[error("conflicting promise")]
Supplanted(CoordNumOf<I>),
#[error("round already converged")]
Converged(CoordNumOf<I>, Option<(CoordNumOf<I>, Arc<LogEntryOf<I>>)>),
#[error("node is passive")]
Passive,
#[error("node is shut down")]
ShutDown,
}
impl<I: Invocation> std::fmt::Debug for PrepareError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PrepareError::Abstained(abstention) => f
.debug_tuple("PrepareError::Abstained")
.field(abstention)
.finish(),
PrepareError::Supplanted(coord_num) => f
.debug_tuple("PrepareError::Supplanted")
.field(coord_num)
.finish(),
PrepareError::Converged(coord_num, converged) => f
.debug_tuple("PrepareError::Converged")
.field(coord_num)
.field(converged)
.finish(),
PrepareError::Passive => f.debug_tuple("PrepareError::Passive").finish(),
PrepareError::ShutDown => f.debug_tuple("PrepareError::ShutDown").finish(),
}
}
}
impl<I: Invocation> From<PrepareError<I>> for AppendError<I> {
fn from(e: PrepareError<I>) -> Self {
match e {
PrepareError::Abstained(reason) => AppendError::NoQuorum {
abstentions: vec![reason],
communication_errors: Vec::new(),
discards: Vec::new(),
rejections: Vec::new(),
},
PrepareError::Supplanted(_) => AppendError::Lost,
PrepareError::Converged(_, log_entry) => AppendError::Converged {
caught_up: log_entry.is_some(),
},
PrepareError::Passive => AppendError::Passive,
PrepareError::ShutDown => AppendError::ShutDown,
}
}
}
#[derive(Error)]
pub enum AcceptError<I: Invocation> {
#[error("conflicting promise")]
Supplanted(CoordNumOf<I>),
#[error("round already converged")]
Converged(CoordNumOf<I>, Option<(CoordNumOf<I>, Arc<LogEntryOf<I>>)>),
#[error("node is passive")]
Passive,
#[error("proposal was rejected")]
Rejected(NayOf<I>),
#[error("node is shut down")]
ShutDown,
}
impl<I: Invocation> std::fmt::Debug for AcceptError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AcceptError::Supplanted(coord_num) => f
.debug_tuple("AcceptError::Conflict")
.field(coord_num)
.finish(),
AcceptError::Converged(coord_num, converged) => f
.debug_tuple("AcceptError::Converged")
.field(coord_num)
.field(converged)
.finish(),
AcceptError::Passive => f.debug_tuple("AcceptError::Passive").finish(),
AcceptError::Rejected(rejection) => f
.debug_tuple("AcceptError::Rejected")
.field(rejection)
.finish(),
AcceptError::ShutDown => f.debug_tuple("AcceptError::ShutDown").finish(),
}
}
}
impl<I: Invocation> From<AcceptError<I>> for AppendError<I> {
fn from(e: AcceptError<I>) -> Self {
match e {
AcceptError::Supplanted(_) => AppendError::Lost,
AcceptError::Converged(_, log_entry) => AppendError::Converged {
caught_up: log_entry.is_some(),
},
AcceptError::Passive => AppendError::Passive,
AcceptError::Rejected(reason) => AppendError::Unacceptable(reason),
AcceptError::ShutDown => AppendError::ShutDown,
}
}
}
#[non_exhaustive]
#[derive(Error)]
pub enum CommitError<I: Invocation> {
#[error("node is disoriented")]
Disoriented,
#[error("given log entry id is invalid")]
InvalidEntryId(LogEntryIdOf<I>),
#[error("node is shut down")]
ShutDown,
}
impl<I: Invocation> std::fmt::Debug for CommitError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommitError::Disoriented => f.debug_tuple("CommitError::Disoriented").finish(),
CommitError::InvalidEntryId(entry_id) => f
.debug_tuple("CommitError::InvalidEntryId")
.field(entry_id)
.finish(),
CommitError::ShutDown => f.debug_tuple("CommitError::ShutDown").finish(),
}
}
}
impl<I: Invocation> From<CommitError<I>> for AppendError<I> {
fn from(e: CommitError<I>) -> Self {
match e {
CommitError::Disoriented => AppendError::Disoriented,
CommitError::InvalidEntryId(_) => unreachable!(),
CommitError::ShutDown => AppendError::ShutDown,
}
}
}
impl<I: Invocation> From<CommitError<I>> for PollError<I> {
fn from(e: CommitError<I>) -> Self {
match e {
CommitError::Disoriented => PollError::Disoriented,
CommitError::InvalidEntryId(_) => unreachable!(),
CommitError::ShutDown => PollError::ShutDown,
}
}
}
#[derive(Clone, Copy, Debug, Error)]
#[error("node is disoriented")]
pub struct Disoriented;
#[derive(Clone, Copy, Debug, Error)]
#[error("node is shut down")]
pub struct ShutDown;
impl From<Infallible> for ShutDown {
fn from(_: Infallible) -> Self {
Self
}
}
impl From<ShutDownOr<Infallible>> for ShutDown {
fn from(_: ShutDownOr<Infallible>) -> Self {
Self
}
}
#[derive(Clone, Copy, Debug)]
pub enum ShutDownOr<E> {
Other(E),
ShutDown,
}
impl<E> ShutDownOr<E> {
pub fn map<T, F: FnOnce(E) -> T>(self, f: F) -> ShutDownOr<T> {
match self {
ShutDownOr::Other(e) => ShutDownOr::Other(f(e)),
ShutDownOr::ShutDown => ShutDownOr::ShutDown,
}
}
pub fn expect_other(self) -> E {
match self {
ShutDownOr::Other(e) => e,
ShutDownOr::ShutDown => panic!("Node is unexpectedly shut down."),
}
}
}
impl<E> From<E> for ShutDownOr<E> {
fn from(e: E) -> Self {
ShutDownOr::Other(e)
}
}
#[derive(Error)]
#[non_exhaustive]
pub enum PollError<I: Invocation> {
#[error("a node decoration raised an error")]
Decoration(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
#[error("node is disoriented")]
Disoriented,
#[error("round has converged locally")]
LocallyConverged,
#[error("log entry is no longer retained")]
NotRetained,
#[error("node is shut down")]
ShutDown,
#[error("received no useful responses")]
UselessResponses {
abstentions: Vec<AbstainOf<I>>,
communication_errors: Vec<CommunicationErrorOf<I>>,
},
}
impl<I: Invocation> std::fmt::Debug for PollError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PollError::Decoration(err) => {
f.debug_tuple("PollError::Decoration").field(err).finish()
}
PollError::Disoriented => f.debug_tuple("PollError::Disoriented").finish(),
PollError::LocallyConverged => f.debug_tuple("PollError::LocallyConverged").finish(),
PollError::NotRetained => f.debug_tuple("PollError::NotRetained").finish(),
PollError::ShutDown => f.debug_tuple("PollError::ShutDown").finish(),
PollError::UselessResponses {
abstentions,
communication_errors,
} => f
.debug_struct("PollError::UselessResponses")
.field("abstentions", abstentions)
.field("communication_errors", communication_errors)
.finish(),
}
}
}