redis/errors/
server_error.rs1use arcstr::ArcStr;
2use std::fmt;
3
4use crate::RetryMethod;
5
6#[derive(PartialEq, Debug, Clone, Copy, Eq)]
8#[non_exhaustive]
9pub enum ServerErrorKind {
10 ResponseError,
12 ExecAbort,
14 BusyLoading,
16 NoScript,
18 Moved,
20 Ask,
22 TryAgain,
24 ClusterDown,
26 CrossSlot,
28 MasterDown,
30 ReadOnly,
32 NotBusy,
34 NoSub,
36 NoPerm,
38}
39
40impl ServerErrorKind {
41 pub(crate) fn code(self) -> &'static str {
42 match self {
43 Self::ResponseError => "ERR",
44 Self::ExecAbort => "EXECABORT",
45 Self::BusyLoading => "LOADING",
46 Self::NoScript => "NOSCRIPT",
47 Self::Moved => "MOVED",
48 Self::Ask => "ASK",
49 Self::TryAgain => "TRYAGAIN",
50 Self::ClusterDown => "CLUSTERDOWN",
51 Self::CrossSlot => "CROSSSLOT",
52 Self::MasterDown => "MASTERDOWN",
53 Self::ReadOnly => "READONLY",
54 Self::NotBusy => "NOTBUSY",
55 Self::NoSub => "NOSUB",
56 Self::NoPerm => "NOPERM",
57 }
58 }
59
60 pub(crate) fn retry_method(self) -> RetryMethod {
61 match self {
62 Self::Moved => RetryMethod::MovedRedirect,
63 Self::Ask => RetryMethod::AskRedirect,
64
65 Self::TryAgain | Self::MasterDown | Self::ClusterDown | Self::BusyLoading => {
66 RetryMethod::WaitAndRetry
67 }
68
69 Self::ReadOnly => RetryMethod::RefreshSlotsAndRetry,
72
73 Self::ResponseError
74 | Self::ExecAbort
75 | Self::NoScript
76 | Self::CrossSlot
77 | Self::NotBusy
78 | Self::NoSub
79 | Self::NoPerm => RetryMethod::NoRetry,
80 }
81 }
82}
83
84#[derive(PartialEq, Debug, Clone)]
86pub struct ServerError(pub(crate) Repr);
87
88#[derive(PartialEq, Debug, Clone)]
89pub(crate) enum Repr {
90 Extension {
91 code: ArcStr,
92 detail: Option<ArcStr>,
93 },
94 Known {
95 kind: ServerErrorKind,
96 detail: Option<ArcStr>,
97 },
98}
99
100impl ServerError {
101 pub fn kind(&self) -> Option<ServerErrorKind> {
103 match &self.0 {
104 Repr::Extension { .. } => None,
105 Repr::Known { kind, .. } => Some(*kind),
106 }
107 }
108
109 pub fn code(&self) -> &str {
111 match &self.0 {
112 Repr::Extension { code, .. } => code,
113 Repr::Known { kind, .. } => kind.code(),
114 }
115 }
116
117 pub fn details(&self) -> Option<&str> {
119 match &self.0 {
120 Repr::Extension { detail, .. } => detail.as_ref().map(|str| str.as_str()),
121 Repr::Known { detail, .. } => detail.as_ref().map(|str| str.as_str()),
122 }
123 }
124
125 #[cfg(feature = "cluster-async")]
126 pub(crate) fn requires_action(&self) -> bool {
127 !matches!(
128 self.kind()
129 .map(|kind| kind.retry_method())
130 .unwrap_or(RetryMethod::NoRetry),
131 RetryMethod::NoRetry
132 )
133 }
134}
135
136impl fmt::Display for ServerError {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match &self.0 {
139 Repr::Extension { code, detail } => {
140 fmt::Debug::fmt(&code, f)?;
141 if let Some(detail) = detail {
142 f.write_str(": ")?;
143 detail.fmt(f)?;
144 }
145 Ok(())
146 }
147 Repr::Known { kind, detail } => {
148 fmt::Debug::fmt(&kind, f)?;
149 if let Some(detail) = detail {
150 f.write_str(": ")?;
151 detail.fmt(f)?;
152 }
153 Ok(())
154 }
155 }
156 }
157}
158
159impl std::error::Error for ServerError {}