redis_async/
error.rs

1/*
2 * Copyright 2017-2023 Ben Ashford
3 *
4 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 * option. This file may not be copied, modified, or distributed
8 * except according to those terms.
9 */
10
11//! Error handling
12
13use std::error;
14use std::fmt;
15use std::io;
16use std::sync::Arc;
17
18use futures_channel::mpsc;
19
20use crate::resp;
21
22#[derive(Debug, Clone)]
23pub enum Error {
24    /// A non-specific internal error that prevented an operation from completing
25    Internal(String),
26
27    /// An IO error occurred
28    IO(Arc<io::Error>),
29
30    /// A RESP parsing/serialising error occurred
31    Resp(String, Option<resp::RespValue>),
32
33    /// A remote error
34    Remote(String),
35
36    /// Error creating a connection, or an error with a connection being closed unexpectedly
37    Connection(ConnectionReason),
38
39    /// An unexpected error.  In this context "unexpected" means
40    /// "unexpected because we check ahead of time", it used to maintain the type signature of
41    /// chains of futures; but it occurring at runtime should be considered a catastrophic
42    /// failure.
43    ///
44    /// If any error is propagated this way that needs to be handled, then it should be made into
45    /// a proper option.
46    Unexpected(String),
47
48    #[cfg(feature = "with-rustls")]
49    InvalidDnsName,
50
51    #[cfg(feature = "with-native-tls")]
52    Tls(Arc<native_tls::Error>),
53}
54
55pub(crate) fn internal(msg: impl Into<String>) -> Error {
56    Error::Internal(msg.into())
57}
58
59pub(crate) fn unexpected(msg: impl Into<String>) -> Error {
60    Error::Unexpected(msg.into())
61}
62
63pub(crate) fn resp(msg: impl Into<String>, resp: resp::RespValue) -> Error {
64    Error::Resp(msg.into(), Some(resp))
65}
66
67impl From<io::Error> for Error {
68    fn from(err: io::Error) -> Error {
69        Error::IO(Arc::new(err))
70    }
71}
72
73impl<T: 'static + Send> From<mpsc::TrySendError<T>> for Error {
74    fn from(err: mpsc::TrySendError<T>) -> Error {
75        Error::Unexpected(format!("Cannot write to channel: {}", err))
76    }
77}
78
79impl error::Error for Error {
80    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
81        match self {
82            Error::IO(err) => Some(err),
83            #[cfg(feature = "with-native-tls")]
84            Error::Tls(err) => Some(err),
85            _ => None,
86        }
87    }
88}
89
90#[cfg(feature = "with-native-tls")]
91impl From<native_tls::Error> for Error {
92    fn from(err: native_tls::Error) -> Error {
93        Error::Tls(Arc::new(err))
94    }
95}
96
97impl fmt::Display for Error {
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        match self {
100            Error::Internal(s) => write!(f, "{}", s),
101            Error::IO(err) => write!(f, "{}", err),
102            Error::Resp(s, resp) => write!(f, "{}: {:?}", s, resp),
103            Error::Remote(s) => write!(f, "{}", s),
104            Error::Connection(ConnectionReason::Connected) => {
105                write!(f, "Connection already established")
106            }
107            Error::Connection(ConnectionReason::Connecting) => write!(f, "Connection in progress"),
108            Error::Connection(ConnectionReason::ConnectionFailed) => {
109                write!(f, "The last attempt to establish a connection failed")
110            }
111            Error::Connection(ConnectionReason::NotConnected) => {
112                write!(f, "Connection has been closed")
113            }
114            #[cfg(feature = "with-rustls")]
115            Error::InvalidDnsName => {
116                write!(f, "Invalid dns name")
117            }
118            #[cfg(feature = "with-native-tls")]
119            Error::Tls(err) => write!(f, "{}", err),
120            Error::Unexpected(err) => write!(f, "{}", err),
121        }
122    }
123}
124
125/// Details of a `ConnectionError`
126#[derive(Debug, Copy, Clone)]
127pub enum ConnectionReason {
128    /// An attempt to use a connection while it is in the "connecting" state, clients should try
129    /// again
130    Connecting,
131    /// An attempt was made to reconnect after a connection was established, clients should try
132    /// again
133    Connected,
134    /// Connection failed - this can be returned from a call to reconnect, the actual error will be
135    /// sent to the client at the next call
136    ConnectionFailed,
137    /// The connection is not currently connected, the connection will reconnect asynchronously,
138    /// clients should try again
139    NotConnected,
140}