1use std::fmt;
11use std::io;
12use std::str::Utf8Error;
13use std::sync::PoisonError;
14
15use crate::response::VoltResponseInfo;
16
17#[derive(Debug)]
19pub enum ConnectionError {
20 Io(io::Error),
22 NotAvailable,
24 Timeout,
26 AuthFailed,
28 InvalidConfig,
30}
31
32impl fmt::Display for ConnectionError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 ConnectionError::Io(e) => write!(f, "I/O error: {}", e),
36 ConnectionError::NotAvailable => write!(f, "Connection not available"),
37 ConnectionError::Timeout => write!(f, "Connection timeout"),
38 ConnectionError::AuthFailed => write!(f, "Authentication failed"),
39 ConnectionError::InvalidConfig => write!(f, "Invalid configuration"),
40 }
41 }
42}
43
44impl std::error::Error for ConnectionError {
45 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46 match self {
47 ConnectionError::Io(e) => Some(e),
48 _ => None,
49 }
50 }
51}
52
53impl From<io::Error> for ConnectionError {
54 fn from(err: io::Error) -> Self {
55 ConnectionError::Io(err)
56 }
57}
58
59#[derive(Debug)]
61pub enum ProtocolError {
62 InvalidColumnType(i8),
64 NegativeNumTables(i16),
66 BadReturnStatusOnTable(i8),
68 Utf8(Utf8Error),
70 Other(String),
72}
73
74impl fmt::Display for ProtocolError {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 match self {
77 ProtocolError::InvalidColumnType(t) => write!(f, "Invalid column type: {}", t),
78 ProtocolError::NegativeNumTables(n) => write!(f, "Negative number of tables: {}", n),
79 ProtocolError::BadReturnStatusOnTable(s) => {
80 write!(f, "Bad return status on table: {}", s)
81 }
82 ProtocolError::Utf8(e) => write!(f, "UTF-8 error: {}", e),
83 ProtocolError::Other(s) => write!(f, "Protocol error: {}", s),
84 }
85 }
86}
87
88impl std::error::Error for ProtocolError {
89 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90 match self {
91 ProtocolError::Utf8(e) => Some(e),
92 _ => None,
93 }
94 }
95}
96
97impl From<Utf8Error> for ProtocolError {
98 fn from(err: Utf8Error) -> Self {
99 ProtocolError::Utf8(err)
100 }
101}
102
103#[derive(Debug)]
105pub enum QueryError {
106 ExecuteFail(VoltResponseInfo),
108 NoValue(String),
110 UnexpectedNull(String),
112 RecvError(std::sync::mpsc::RecvError),
114}
115
116impl fmt::Display for QueryError {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 QueryError::ExecuteFail(info) => write!(f, "Query execution failed: {:?}", info),
120 QueryError::NoValue(col) => write!(f, "No value found for: {}", col),
121 QueryError::UnexpectedNull(col) => {
122 write!(
123 f,
124 "Unexpected NULL in column '{}'. Use Option<T> for nullable columns.",
125 col
126 )
127 }
128 QueryError::RecvError(e) => write!(f, "Receive error: {}", e),
129 }
130 }
131}
132
133impl std::error::Error for QueryError {
134 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135 match self {
136 QueryError::RecvError(e) => Some(e),
137 _ => None,
138 }
139 }
140}
141
142impl From<std::sync::mpsc::RecvError> for QueryError {
143 fn from(err: std::sync::mpsc::RecvError) -> Self {
144 QueryError::RecvError(err)
145 }
146}
147
148#[derive(Debug)]
150pub struct ConcurrencyError(pub String);
151
152impl fmt::Display for ConcurrencyError {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 write!(f, "Concurrency error: {}", self.0)
155 }
156}
157
158impl std::error::Error for ConcurrencyError {}
159
160impl<T> From<PoisonError<T>> for ConcurrencyError {
161 fn from(err: PoisonError<T>) -> Self {
162 ConcurrencyError(err.to_string())
163 }
164}
165
166pub trait IntoVoltError {
168 fn into_volt_error(self) -> crate::encode::VoltError;
169}
170
171impl IntoVoltError for ConnectionError {
172 fn into_volt_error(self) -> crate::encode::VoltError {
173 match self {
174 ConnectionError::Io(e) => crate::encode::VoltError::Io(e),
175 ConnectionError::NotAvailable => crate::encode::VoltError::ConnectionNotAvailable,
176 ConnectionError::Timeout => crate::encode::VoltError::Timeout,
177 ConnectionError::AuthFailed => crate::encode::VoltError::AuthFailed,
178 ConnectionError::InvalidConfig => crate::encode::VoltError::InvalidConfig,
179 }
180 }
181}
182
183impl IntoVoltError for ProtocolError {
184 fn into_volt_error(self) -> crate::encode::VoltError {
185 match self {
186 ProtocolError::InvalidColumnType(t) => crate::encode::VoltError::InvalidColumnType(t),
187 ProtocolError::NegativeNumTables(n) => crate::encode::VoltError::NegativeNumTables(n),
188 ProtocolError::BadReturnStatusOnTable(s) => {
189 crate::encode::VoltError::BadReturnStatusOnTable(s)
190 }
191 ProtocolError::Utf8(e) => crate::encode::VoltError::Utf8Error(e),
192 ProtocolError::Other(s) => crate::encode::VoltError::Other(s),
193 }
194 }
195}
196
197impl IntoVoltError for QueryError {
198 fn into_volt_error(self) -> crate::encode::VoltError {
199 match self {
200 QueryError::ExecuteFail(info) => crate::encode::VoltError::ExecuteFail(info),
201 QueryError::NoValue(s) => crate::encode::VoltError::NoValue(s),
202 QueryError::UnexpectedNull(s) => crate::encode::VoltError::UnexpectedNull(s),
203 QueryError::RecvError(e) => crate::encode::VoltError::RecvError(e),
204 }
205 }
206}
207
208impl IntoVoltError for ConcurrencyError {
209 fn into_volt_error(self) -> crate::encode::VoltError {
210 crate::encode::VoltError::PoisonError(self.0)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use std::error::Error;
218
219 #[test]
221 fn test_connection_error_display() {
222 let err = ConnectionError::NotAvailable;
223 assert_eq!(format!("{}", err), "Connection not available");
224 }
225
226 #[test]
227 fn test_connection_error_timeout() {
228 let err = ConnectionError::Timeout;
229 assert_eq!(format!("{}", err), "Connection timeout");
230 }
231
232 #[test]
233 fn test_connection_error_auth_failed() {
234 let err = ConnectionError::AuthFailed;
235 assert_eq!(format!("{}", err), "Authentication failed");
236 }
237
238 #[test]
239 fn test_connection_error_invalid_config() {
240 let err = ConnectionError::InvalidConfig;
241 assert_eq!(format!("{}", err), "Invalid configuration");
242 }
243
244 #[test]
245 fn test_connection_error_from_io() {
246 let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "refused");
247 let conn_err: ConnectionError = io_err.into();
248 assert!(matches!(conn_err, ConnectionError::Io(_)));
249 assert!(format!("{}", conn_err).contains("I/O error"));
250 }
251
252 #[test]
254 fn test_protocol_error_display() {
255 let err = ProtocolError::InvalidColumnType(99);
256 assert_eq!(format!("{}", err), "Invalid column type: 99");
257 }
258
259 #[test]
260 fn test_protocol_error_negative_tables() {
261 let err = ProtocolError::NegativeNumTables(-5);
262 assert!(format!("{}", err).contains("-5"));
263 }
264
265 #[test]
266 fn test_protocol_error_bad_status() {
267 let err = ProtocolError::BadReturnStatusOnTable(-1);
268 assert!(format!("{}", err).contains("-1"));
269 }
270
271 #[test]
272 fn test_protocol_error_other() {
273 let err = ProtocolError::Other("custom error".to_string());
274 assert!(format!("{}", err).contains("custom error"));
275 }
276
277 #[test]
279 fn test_query_error_display() {
280 let err = QueryError::UnexpectedNull("my_column".to_string());
281 assert!(format!("{}", err).contains("my_column"));
282 }
283
284 #[test]
285 fn test_query_error_no_value() {
286 let err = QueryError::NoValue("missing_col".to_string());
287 assert!(format!("{}", err).contains("missing_col"));
288 }
289
290 #[test]
291 fn test_query_error_unexpected_null_message() {
292 let err = QueryError::UnexpectedNull("test_col".to_string());
293 let msg = format!("{}", err);
294 assert!(msg.contains("test_col"));
295 assert!(msg.contains("Option<T>"));
296 }
297
298 #[test]
300 fn test_concurrency_error_display() {
301 let err = ConcurrencyError("lock poisoned".to_string());
302 assert!(format!("{}", err).contains("lock poisoned"));
303 }
304
305 #[test]
307 fn test_connection_error_into_volt_error() {
308 let conn_err = ConnectionError::NotAvailable;
309 let volt_err = conn_err.into_volt_error();
310 assert!(matches!(
311 volt_err,
312 crate::encode::VoltError::ConnectionNotAvailable
313 ));
314 }
315
316 #[test]
317 fn test_connection_timeout_into_volt_error() {
318 let conn_err = ConnectionError::Timeout;
319 let volt_err = conn_err.into_volt_error();
320 assert!(matches!(volt_err, crate::encode::VoltError::Timeout));
321 }
322
323 #[test]
324 fn test_protocol_error_into_volt_error() {
325 let proto_err = ProtocolError::InvalidColumnType(42);
326 let volt_err = proto_err.into_volt_error();
327 match volt_err {
328 crate::encode::VoltError::InvalidColumnType(t) => assert_eq!(t, 42),
329 _ => panic!("Expected InvalidColumnType"),
330 }
331 }
332
333 #[test]
334 fn test_query_error_into_volt_error() {
335 let query_err = QueryError::NoValue("col".to_string());
336 let volt_err = query_err.into_volt_error();
337 match volt_err {
338 crate::encode::VoltError::NoValue(s) => assert_eq!(s, "col"),
339 _ => panic!("Expected NoValue"),
340 }
341 }
342
343 #[test]
344 fn test_concurrency_error_into_volt_error() {
345 let conc_err = ConcurrencyError("poison".to_string());
346 let volt_err = conc_err.into_volt_error();
347 match volt_err {
348 crate::encode::VoltError::PoisonError(s) => assert_eq!(s, "poison"),
349 _ => panic!("Expected PoisonError"),
350 }
351 }
352
353 #[test]
355 fn test_connection_error_source() {
356 let io_err = io::Error::other("test");
357 let conn_err = ConnectionError::Io(io_err);
358 assert!(conn_err.source().is_some());
359
360 let conn_err = ConnectionError::NotAvailable;
361 assert!(conn_err.source().is_none());
362 }
363
364 #[test]
365 fn test_protocol_error_source() {
366 let proto_err = ProtocolError::InvalidColumnType(1);
367 assert!(proto_err.source().is_none());
368 }
369
370 #[test]
371 fn test_query_error_source() {
372 let query_err = QueryError::NoValue("x".to_string());
373 assert!(query_err.source().is_none());
374 }
375}