redis_driver/
error.rs

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/// `Internal Use`
14/// 
15/// Gives a reason to retry sending a command to the Redis Server
16#[derive(Debug)]
17pub enum RetryReason {
18    /// Received an ASK error from the Redis Server
19    Ask {
20        hash_slot: u16,
21        address: (String, u16),
22    },
23    /// Received a MOVED error from the Redis Server
24    Moved {
25        hash_slot: u16,
26        address: (String, u16),
27    },
28}
29
30/// All error kinds
31#[derive(Debug)]
32pub enum Error {
33    /// Raised if an error occurs within the driver
34    Client(String),
35    /// Raised if an error occurs in the [`Config`](crate::Config) parsing
36    Config(String),
37    /// A transaction has been aborted
38    Aborted,
39    /// Raised if an error occurs when contacting Sentinel instances
40    Sentinel(String),
41    /// Error returned by the Redis sercer
42    Redis(RedisError),
43    /// IO error when connecting the Redis server
44    IO(std::io::Error),
45    #[cfg(feature = "tls")]
46    /// Raised by the TLS library
47    Tls(String),
48    /// Internal error to trigger retry sending the command
49    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/// Redis server error kind
112#[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/// Error issued by the Redis server
246#[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}