clickhouse_srv/
errors.rs

1use std::borrow::Cow;
2use std::io;
3use std::result;
4use std::str::Utf8Error;
5use std::string::FromUtf8Error;
6
7use thiserror::Error;
8#[cfg(feature = "tokio_io")]
9use tokio::time::error::Elapsed;
10use url::ParseError;
11
12/// Result type alias for this library.
13pub type Result<T> = result::Result<T, Error>;
14
15/// This type enumerates library errors.
16#[derive(Debug, Error)]
17pub enum Error {
18    #[error("Driver error: `{}`", _0)]
19    Driver(#[source] DriverError),
20
21    #[error("Input/output error: `{}`", _0)]
22    IO(#[source] io::Error),
23
24    #[error("Connections error: `{}`", _0)]
25    Connection(#[source] ConnectionError),
26
27    #[error("Other error: `{}`", _0)]
28    Other(Cow<'static, str>),
29
30    #[error("Server error: `{}`", _0)]
31    Server(#[source] ServerError),
32
33    #[error("URL error: `{}`", _0)]
34    Url(#[source] UrlError),
35
36    #[error("From SQL error: `{}`", _0)]
37    FromSql(#[source] FromSqlError)
38}
39
40/// This type represents Clickhouse server error.
41#[derive(Debug, Error, Clone)]
42#[error("ERROR {} ({:?}): {}", name, code, message)]
43pub struct ServerError {
44    pub code: u32,
45    pub name: String,
46    pub message: String,
47    pub stack_trace: String
48}
49
50/// This type enumerates connection errors.
51#[derive(Debug, Error)]
52pub enum ConnectionError {
53    #[error("TLS connection requires hostname to be provided")]
54    TlsHostNotProvided,
55
56    #[error("Input/output error: `{}`", _0)]
57    IOError(#[source] io::Error),
58
59    #[cfg(feature = "tls")]
60    #[error("TLS connection error: `{}`", _0)]
61    TlsError(#[source] native_tls::Error)
62}
63
64/// This type enumerates connection URL errors.
65#[derive(Debug, Error, Clone)]
66pub enum UrlError {
67    #[error("Invalid or incomplete connection URL")]
68    Invalid,
69
70    #[error("Invalid value `{}' for connection URL parameter `{}'", value, param)]
71    InvalidParamValue { param: String, value: String },
72
73    #[error("URL parse error: {}", _0)]
74    Parse(#[source] ParseError),
75
76    #[error("Unknown connection URL parameter `{}'", param)]
77    UnknownParameter { param: String },
78
79    #[error("Unsupported connection URL scheme `{}'", scheme)]
80    UnsupportedScheme { scheme: String }
81}
82
83/// This type enumerates driver errors.
84#[derive(Debug, Error, Clone)]
85pub enum DriverError {
86    #[error("Varint overflows a 64-bit integer.")]
87    Overflow,
88
89    #[error("Unknown packet 0x{:x}.", packet)]
90    UnknownPacket { packet: u64 },
91
92    #[error("Unexpected packet.")]
93    UnexpectedPacket,
94
95    #[error("Timeout error.")]
96    Timeout,
97
98    #[error("Invalid utf-8 sequence.")]
99    Utf8Error(Utf8Error),
100
101    #[error("UnknownSetting name {}", name)]
102    UnknownSetting { name: String }
103}
104
105/// This type enumerates cast from sql type errors.
106#[derive(Debug, Error, Clone)]
107pub enum FromSqlError {
108    #[error("SqlType::{} cannot be cast to {}.", src, dst)]
109    InvalidType {
110        src: Cow<'static, str>,
111        dst: Cow<'static, str>
112    },
113
114    #[error("Out of range.")]
115    OutOfRange,
116
117    #[error("Unsupported operation.")]
118    UnsupportedOperation
119}
120
121impl Error {
122    pub(crate) fn is_would_block(&self) -> bool {
123        if let Error::IO(ref e) = self {
124            if e.kind() == io::ErrorKind::WouldBlock {
125                return true;
126            }
127        }
128        false
129    }
130}
131
132impl From<ConnectionError> for Error {
133    fn from(error: ConnectionError) -> Self {
134        Error::Connection(error)
135    }
136}
137
138#[cfg(feature = "tls")]
139impl From<native_tls::Error> for ConnectionError {
140    fn from(error: native_tls::Error) -> Self {
141        ConnectionError::TlsError(error)
142    }
143}
144
145impl From<DriverError> for Error {
146    fn from(err: DriverError) -> Self {
147        Error::Driver(err)
148    }
149}
150
151impl From<io::Error> for Error {
152    fn from(err: io::Error) -> Self {
153        Error::IO(err)
154    }
155}
156
157impl From<Error> for io::Error {
158    fn from(err: Error) -> Self {
159        match err {
160            Error::IO(error) => error,
161            e => io::Error::new(io::ErrorKind::Other, e.to_string())
162        }
163    }
164}
165
166impl From<ServerError> for Error {
167    fn from(err: ServerError) -> Self {
168        Error::Server(err)
169    }
170}
171
172impl From<UrlError> for Error {
173    fn from(err: UrlError) -> Self {
174        Error::Url(err)
175    }
176}
177
178impl From<String> for Error {
179    fn from(err: String) -> Self {
180        Error::Other(Cow::from(err))
181    }
182}
183
184impl From<&str> for Error {
185    fn from(err: &str) -> Self {
186        Error::Other(err.to_string().into())
187    }
188}
189
190impl From<FromUtf8Error> for Error {
191    fn from(err: FromUtf8Error) -> Self {
192        Error::Other(err.to_string().into())
193    }
194}
195
196#[cfg(feature = "tokio_io")]
197impl From<Elapsed> for Error {
198    fn from(_err: Elapsed) -> Self {
199        Error::Driver(DriverError::Timeout)
200    }
201}
202
203impl From<ParseError> for Error {
204    fn from(err: ParseError) -> Self {
205        Error::Url(UrlError::Parse(err))
206    }
207}
208
209impl From<Utf8Error> for Error {
210    fn from(err: Utf8Error) -> Self {
211        Error::Driver(DriverError::Utf8Error(err))
212    }
213}
214
215impl Error {
216    pub fn exception_name(&self) -> &str {
217        match self {
218            Error::Driver(_) => "DriverException",
219            Error::IO(_) => "IOException",
220            Error::Connection(_) => "ConnectionException",
221            Error::Other(_) => "OtherException",
222            Error::Server(e) => e.name.as_str(),
223            Error::Url(_) => "URLException",
224            Error::FromSql(_) => "SQLException",
225        }
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    #[test]
232    fn to_std_error_without_recursion() {
233        let src_err: super::Error = From::from("Somth went wrong.");
234        let dst_err: Box<dyn std::error::Error> = src_err.into();
235        assert_eq!(dst_err.to_string(), "Other error: `Somth went wrong.`");
236    }
237
238    #[test]
239    fn to_io_error_without_recursion() {
240        let src_err: super::Error = From::from("Somth went wrong.");
241        let dst_err: std::io::Error = src_err.into();
242        assert_eq!(dst_err.to_string(), "Other error: `Somth went wrong.`");
243    }
244}