1use crate::{Message, Result};
2use futures::channel::{
3 mpsc::{self, TrySendError},
4 oneshot,
5};
6use smallvec::SmallVec;
7use std::{
8 fmt::{Display, Formatter},
9 num::ParseFloatError,
10 str::{FromStr, Utf8Error},
11};
12
13#[derive(Debug)]
17pub enum RetryReason {
18 Ask {
20 hash_slot: u16,
21 address: (String, u16),
22 },
23 Moved {
25 hash_slot: u16,
26 address: (String, u16),
27 },
28}
29
30#[derive(Debug)]
32pub enum Error {
33 Client(String),
35 Config(String),
37 Aborted,
39 Sentinel(String),
41 Redis(RedisError),
43 IO(std::io::Error),
45 #[cfg(feature = "tls")]
46 Tls(String),
48 Retry(SmallVec<[RetryReason; 1]>),
50}
51
52impl std::fmt::Display for Error {
53 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54 match self {
55 Error::Client(e) => f.write_fmt(format_args!("Client error: {}", e)),
56 Error::Config(e) => f.write_fmt(format_args!("Config error: {}", e)),
57 Error::Aborted => f.write_fmt(format_args!("Transaction aborted")),
58 Error::Sentinel(e) => f.write_fmt(format_args!("Sentinel error: {}", e)),
59 Error::Redis(e) => f.write_fmt(format_args!("Redis error: {}", e)),
60 Error::IO(e) => f.write_fmt(format_args!("IO erro: {}", e)),
61 #[cfg(feature = "tls")]
62 Error::Tls(e) => f.write_fmt(format_args!("Tls error: {}", e)),
63 Error::Retry(r) => f.write_fmt(format_args!("Retry: {:?}", r)),
64 }
65 }
66}
67
68impl From<std::io::Error> for Error {
69 fn from(e: std::io::Error) -> Self {
70 Error::IO(e)
71 }
72}
73
74impl From<TrySendError<Message>> for Error {
75 fn from(e: TrySendError<Message>) -> Self {
76 Error::Client(e.to_string())
77 }
78}
79
80impl From<oneshot::Canceled> for Error {
81 fn from(e: oneshot::Canceled) -> Self {
82 Error::Client(e.to_string())
83 }
84}
85
86impl From<mpsc::SendError> for Error {
87 fn from(e: mpsc::SendError) -> Self {
88 Error::Client(e.to_string())
89 }
90}
91
92impl From<Utf8Error> for Error {
93 fn from(e: Utf8Error) -> Self {
94 Error::Client(e.to_string())
95 }
96}
97
98impl From<ParseFloatError> for Error {
99 fn from(e: ParseFloatError) -> Self {
100 Error::Client(e.to_string())
101 }
102}
103
104#[cfg(feature = "tls")]
105impl From<native_tls::Error> for Error {
106 fn from(e: native_tls::Error) -> Self {
107 Error::Tls(e.to_string())
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
113pub enum RedisErrorKind {
114 Ask {
115 hash_slot: u16,
116 address: (String, u16),
117 },
118 BusyGroup,
119 ClusterDown,
120 CrossSlot,
121 Err,
122 InProg,
123 IoErr,
124 MasterDown,
125 MisConf,
126 Moved {
127 hash_slot: u16,
128 address: (String, u16),
129 },
130 NoAuth,
131 NoGoodSlave,
132 NoMasterLink,
133 NoPerm,
134 NoProto,
135 NoQuorum,
136 NotBusy,
137 OutOfMemory,
138 Readonly,
139 TryAgain,
140 UnKillable,
141 Unblocked,
142 WrongPass,
143 WrongType,
144 Other,
145}
146
147impl RedisErrorKind {
148 fn parse_hash_slot_and_address(hash_slot: &str, address: &str) -> Result<(u16, (String, u16))> {
149 let hash_slot = hash_slot
150 .parse::<u16>()
151 .map_err(|_| Error::Client("Cannot parse hash slot".to_owned()))?;
152 let (host, port) = address
153 .split_once(':')
154 .ok_or_else(|| Error::Client("Cannot parse address".to_owned()))?;
155 let port = port
156 .parse::<u16>()
157 .map_err(|_| Error::Client("Cannot parse port".to_owned()))?;
158 Ok((hash_slot, (host.to_owned(), port)))
159 }
160}
161
162impl FromStr for RedisErrorKind {
163 type Err = Error;
164
165 fn from_str(str: &str) -> Result<Self> {
166 match str {
167 "BUSYGROUP" => Ok(Self::BusyGroup),
168 "CLUSTERDOWN" => Ok(Self::ClusterDown),
169 "CROSSSLOT" => Ok(Self::CrossSlot),
170 "ERR" => Ok(Self::Err),
171 "INPROG" => Ok(Self::InProg),
172 "IOERR" => Ok(Self::IoErr),
173 "MASTERDOWN" => Ok(Self::MasterDown),
174 "MISCONF" => Ok(Self::MisConf),
175 "NOAUTH" => Ok(Self::NoAuth),
176 "NOGOODSLAVE" => Ok(Self::NoGoodSlave),
177 "NOMASTERLINK" => Ok(Self::NoMasterLink),
178 "NOPERM" => Ok(Self::NoPerm),
179 "NOPROTO" => Ok(Self::NoProto),
180 "NOQUORUM" => Ok(Self::NoQuorum),
181 "NOTBUSY" => Ok(Self::NotBusy),
182 "OOM" => Ok(Self::OutOfMemory),
183 "READONLY" => Ok(Self::Readonly),
184 "TRYAGAIN" => Ok(Self::TryAgain),
185 "UNKILLABLE" => Ok(Self::UnKillable),
186 "UNBLOCKED" => Ok(Self::Unblocked),
187 "WRONGPASS" => Ok(Self::WrongPass),
188 "WRONGTYPE" => Ok(Self::WrongType),
189 _ => {
190 let mut iter = str.split_whitespace();
191 match (iter.next(), iter.next(), iter.next(), iter.next()) {
192 (Some("ASK"), Some(hash_slot), Some(address), None) => {
193 Self::parse_hash_slot_and_address(hash_slot, address)
194 .map(|(hash_slot, address)| Self::Ask { hash_slot, address })
195 }
196 (Some("MOVED"), Some(hash_slot), Some(address), None) => {
197 Self::parse_hash_slot_and_address(hash_slot, address)
198 .map(|(hash_slot, address)| Self::Moved { hash_slot, address })
199 }
200 _ => Ok(Self::Other),
201 }
202 }
203 }
204 }
205}
206
207impl Display for RedisErrorKind {
208 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209 match self {
210 RedisErrorKind::Ask {
211 hash_slot,
212 address: (host, port),
213 } => f.write_fmt(format_args!("ASK {} {}:{}", *hash_slot, *host, *port)),
214 RedisErrorKind::BusyGroup => f.write_str("BUSYGROUP"),
215 RedisErrorKind::ClusterDown => f.write_str("CLUSTERDOWN"),
216 RedisErrorKind::CrossSlot => f.write_str("CROSSSLOT"),
217 RedisErrorKind::Err => f.write_str("ERR"),
218 RedisErrorKind::InProg => f.write_str("INPROG"),
219 RedisErrorKind::IoErr => f.write_str("IOERR"),
220 RedisErrorKind::MasterDown => f.write_str("MASTERDOWN"),
221 RedisErrorKind::MisConf => f.write_str("MISCONF"),
222 RedisErrorKind::Moved {
223 hash_slot,
224 address: (host, port),
225 } => f.write_fmt(format_args!("MOVED {} {}:{}", *hash_slot, *host, *port)),
226 RedisErrorKind::NoAuth => f.write_str("NOAUTH"),
227 RedisErrorKind::NoGoodSlave => f.write_str("NOGOODSLAVE"),
228 RedisErrorKind::NoMasterLink => f.write_str("NOMASTERLINK"),
229 RedisErrorKind::NoPerm => f.write_str("NOPERM"),
230 RedisErrorKind::NoProto => f.write_str("NOPROTO"),
231 RedisErrorKind::NoQuorum => f.write_str("NOQUORUM"),
232 RedisErrorKind::NotBusy => f.write_str("NOTBUSY"),
233 RedisErrorKind::OutOfMemory => f.write_str("OOM"),
234 RedisErrorKind::Readonly => f.write_str("READONLY"),
235 RedisErrorKind::TryAgain => f.write_str("TRYAGAIN"),
236 RedisErrorKind::UnKillable => f.write_str("UNKILLABLE"),
237 RedisErrorKind::Unblocked => f.write_str("UNBLOCKED"),
238 RedisErrorKind::WrongPass => f.write_str("WRONGPASS"),
239 RedisErrorKind::WrongType => f.write_str("WRONGTYPE"),
240 RedisErrorKind::Other => f.write_str(""),
241 }
242 }
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct RedisError {
248 pub kind: RedisErrorKind,
249 pub description: String,
250}
251
252impl FromStr for RedisError {
253 type Err = Error;
254
255 fn from_str(error: &str) -> Result<Self> {
256 match error.split_once(' ') {
257 Some(("ASK", _)) => Ok(Self {
258 kind: RedisErrorKind::from_str(error)?,
259 description: "".to_owned(),
260 }),
261 Some(("MOVED", _)) => Ok(Self {
262 kind: RedisErrorKind::from_str(error)?,
263 description: "".to_owned(),
264 }),
265 Some((kind, description)) => {
266 let kind = RedisErrorKind::from_str(kind)?;
267
268 let description = if let RedisErrorKind::Other = kind {
269 error.to_owned()
270 } else {
271 description.to_owned()
272 };
273
274 Ok(Self { kind, description })
275 }
276 None => Ok(Self {
277 kind: RedisErrorKind::Other,
278 description: error.to_owned(),
279 }),
280 }
281 }
282}
283
284impl Display for RedisError {
285 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
286 f.write_fmt(format_args!("{} {}", self.kind, self.description))
287 }
288}