use arcstr::ArcStr;
use std::fmt;
use crate::RetryMethod;
#[derive(PartialEq, Debug, Clone, Copy, Eq)]
#[non_exhaustive]
pub enum ServerErrorKind {
ResponseError,
ExecAbort,
BusyLoading,
NoScript,
Moved,
Ask,
TryAgain,
ClusterDown,
CrossSlot,
MasterDown,
ReadOnly,
NotBusy,
NoSub,
NoPerm,
}
impl ServerErrorKind {
pub(crate) fn code(&self) -> &'static str {
match self {
Self::ResponseError => "ERR",
Self::ExecAbort => "EXECABORT",
Self::BusyLoading => "LOADING",
Self::NoScript => "NOSCRIPT",
Self::Moved => "MOVED",
Self::Ask => "ASK",
Self::TryAgain => "TRYAGAIN",
Self::ClusterDown => "CLUSTERDOWN",
Self::CrossSlot => "CROSSSLOT",
Self::MasterDown => "MASTERDOWN",
Self::ReadOnly => "READONLY",
Self::NotBusy => "NOTBUSY",
Self::NoSub => "NOSUB",
Self::NoPerm => "NOPERM",
}
}
pub(crate) fn retry_method(&self) -> RetryMethod {
match self {
Self::Moved => RetryMethod::MovedRedirect,
Self::Ask => RetryMethod::AskRedirect,
Self::TryAgain => RetryMethod::WaitAndRetry,
Self::MasterDown => RetryMethod::WaitAndRetry,
Self::ClusterDown => RetryMethod::WaitAndRetry,
Self::BusyLoading => RetryMethod::WaitAndRetry,
Self::ResponseError => RetryMethod::NoRetry,
Self::ReadOnly => RetryMethod::NoRetry,
Self::ExecAbort => RetryMethod::NoRetry,
Self::NoScript => RetryMethod::NoRetry,
Self::CrossSlot => RetryMethod::NoRetry,
Self::NotBusy => RetryMethod::NoRetry,
Self::NoSub => RetryMethod::NoRetry,
Self::NoPerm => RetryMethod::NoRetry,
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct ServerError(pub(crate) Repr);
#[derive(PartialEq, Debug, Clone)]
pub(crate) enum Repr {
Extension {
code: ArcStr,
detail: Option<ArcStr>,
},
Known {
kind: ServerErrorKind,
detail: Option<ArcStr>,
},
}
impl ServerError {
pub fn kind(&self) -> Option<ServerErrorKind> {
match &self.0 {
Repr::Extension { .. } => None,
Repr::Known { kind, .. } => Some(*kind),
}
}
pub fn code(&self) -> &str {
match &self.0 {
Repr::Extension { code, .. } => code,
Repr::Known { kind, .. } => kind.code(),
}
}
pub fn details(&self) -> Option<&str> {
match &self.0 {
Repr::Extension { detail, .. } => detail.as_ref().map(|str| str.as_str()),
Repr::Known { detail, .. } => detail.as_ref().map(|str| str.as_str()),
}
}
#[cfg(feature = "cluster-async")]
pub(crate) fn requires_action(&self) -> bool {
!matches!(
self.kind()
.map(|kind| kind.retry_method())
.unwrap_or(RetryMethod::NoRetry),
RetryMethod::NoRetry
)
}
}
impl fmt::Display for ServerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Repr::Extension { code, detail } => {
fmt::Debug::fmt(&code, f)?;
if let Some(detail) = detail {
f.write_str(": ")?;
detail.fmt(f)?;
}
Ok(())
}
Repr::Known { kind, detail } => {
fmt::Debug::fmt(&kind, f)?;
if let Some(detail) = detail {
f.write_str(": ")?;
detail.fmt(f)?;
}
Ok(())
}
}
}
}
impl std::error::Error for ServerError {}