1use serde::{de, ser};
4use std::{error::Error as StdError, fmt, io, result, str::Utf8Error};
5
6pub type Result<T, E = Error> = result::Result<T, E>;
8
9type BoxedError = Box<dyn StdError + Send + Sync>;
10
11#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14#[allow(missing_docs)]
15pub enum Error {
16 #[error("invalid params: {0}")]
17 InvalidParams(#[source] BoxedError),
18 #[error("network error: {0}")]
19 Network(#[source] BoxedError),
20 #[error("compression error: {0}")]
21 Compression(#[source] BoxedError),
22 #[error("decompression error: {0}")]
23 Decompression(#[source] BoxedError),
24 #[error("no rows returned by a query that expected to return at least one row")]
25 RowNotFound,
26 #[error("sequences must have a known size ahead of time")]
27 SequenceMustHaveLength,
28 #[error("`deserialize_any` is not supported")]
29 DeserializeAnyNotSupported,
30 #[error("not enough data, probably a row type mismatches a database schema")]
31 NotEnoughData,
32 #[error("string is not valid utf8")]
33 InvalidUtf8Encoding(#[from] Utf8Error),
34 #[error("tag for enum is not valid")]
35 InvalidTagEncoding(usize),
36 #[error("max number of types in the Variant data type is 255, got {0}")]
37 VariantDiscriminatorIsOutOfBound(usize),
38 #[error("a custom error message from serde: {0}")]
39 Custom(String),
40 #[error("bad response: {0}")]
41 BadResponse(String),
42 #[error("timeout expired")]
43 TimedOut,
44 #[error("error while parsing columns header from the response: {0}")]
45 InvalidColumnsHeader(#[source] BoxedError),
46 #[error("schema mismatch: {0}")]
47 SchemaMismatch(String),
48 #[error("unsupported: {0}")]
49 Unsupported(String),
50 #[cfg(feature = "sea-ql")]
51 #[error("type error: {0}")]
52 TypeError(#[from] crate::TypeError),
53 #[error("{0}")]
54 Other(BoxedError),
55}
56
57impl From<clickhouse_types::error::TypesError> for Error {
58 fn from(err: clickhouse_types::error::TypesError) -> Self {
59 Self::InvalidColumnsHeader(Box::new(err))
60 }
61}
62
63impl From<hyper::Error> for Error {
64 fn from(error: hyper::Error) -> Self {
65 Self::Network(Box::new(error))
66 }
67}
68
69impl From<hyper_util::client::legacy::Error> for Error {
70 fn from(error: hyper_util::client::legacy::Error) -> Self {
71 #[cfg(not(any(feature = "rustls-tls", feature = "native-tls")))]
72 if error.is_connect() {
73 static SCHEME_IS_NOT_HTTP: &str = "invalid URL, scheme is not http";
74
75 let src = error.source().unwrap();
76 if src.to_string() == SCHEME_IS_NOT_HTTP {
79 return Self::Unsupported(format!(
80 "{SCHEME_IS_NOT_HTTP}; if you are trying to connect via HTTPS, \
81 consider enabling `native-tls` or `rustls-tls` feature"
82 ));
83 }
84 }
85 Self::Network(Box::new(error))
86 }
87}
88
89impl ser::Error for Error {
90 fn custom<T: fmt::Display>(msg: T) -> Self {
91 Self::Custom(msg.to_string())
92 }
93}
94
95impl de::Error for Error {
96 fn custom<T: fmt::Display>(msg: T) -> Self {
97 Self::Custom(msg.to_string())
98 }
99}
100
101impl From<Error> for io::Error {
102 fn from(error: Error) -> Self {
103 io::Error::other(error)
104 }
105}
106
107impl From<io::Error> for Error {
108 fn from(error: io::Error) -> Self {
109 if error.get_ref().is_some_and(|r| r.is::<Error>()) {
111 *error.into_inner().unwrap().downcast::<Error>().unwrap()
112 } else {
113 Self::Other(error.into())
114 }
115 }
116}
117
118#[cfg(tests)]
119mod tests {
120 use crate::error::Error;
121 use std::io;
122
123 #[test]
124 fn roundtrip_io_error() {
125 let orig = Error::NotEnoughData;
126
127 let orig_str = orig.to_string();
129 let io = io::Error::from(orig);
130 assert_eq!(io.kind(), io::ErrorKind::Other);
131 assert_eq!(io.to_string(), orig_str);
132
133 let orig = Error::from(io);
135 assert!(matches!(orig, Error::NotEnoughData));
136 }
137
138 #[test]
139 fn error_traits() {
140 fn assert_traits<T: std::error::Error + Send + Sync>() {}
141
142 assert_traits::<Error>();
143 }
144}