voltdb_client_rust/
error.rs1use std::sync::PoisonError;
4
5use crate::response::VoltResponseInfo;
6
7#[derive(Debug, thiserror::Error)]
9pub enum VoltError {
10 #[error("I/O error: {0}")]
11 Io(#[from] std::io::Error),
12
13 #[error("Recv error: {0}")]
14 RecvError(#[from] std::sync::mpsc::RecvError),
15
16 #[error("volt execute failed: {0:?}")]
17 ExecuteFail(VoltResponseInfo),
18
19 #[error("InvalidColumnType {0}")]
20 InvalidColumnType(i8),
21
22 #[error("MessageTooLarge {0}")]
23 MessageTooLarge(usize),
24
25 #[error("Error {0}")]
26 NoValue(String),
27
28 #[error("Error {0}")]
29 Other(String),
30
31 #[error("Error {0}")]
32 NegativeNumTables(i16),
33
34 #[error("Utf8 error: {0}")]
35 Utf8Error(#[from] std::str::Utf8Error),
36
37 #[error("Error {0}")]
38 PoisonError(String),
39
40 #[error("Error {0}")]
41 BadReturnStatusOnTable(i8),
42
43 #[error("Auth failed")]
44 AuthFailed,
45
46 #[error("Connection lost")]
47 ConnectionNotAvailable,
48
49 #[error("Invalid Config")]
50 InvalidConfig,
51
52 #[error("Operation timeout")]
53 Timeout,
54
55 #[error("Connection Closed")]
56 ConnectionClosed,
57
58 #[error("Unexpected NULL value in column '{0}'. Use Option<T> for nullable columns.")]
61 UnexpectedNull(String),
62
63 #[error("Pool is shutting down")]
64 PoolShutdown,
65
66 #[error("Circuit breaker is open")]
67 CircuitOpen,
68
69 #[error("Pool exhausted, no healthy connections")]
70 PoolExhausted,
71}
72
73impl<T> From<PoisonError<T>> for VoltError {
74 fn from(p: PoisonError<T>) -> VoltError {
75 VoltError::PoisonError(p.to_string().to_owned())
76 }
77}
78
79impl VoltError {
80 pub fn is_connection_fatal(&self) -> bool {
83 match self {
84 VoltError::Io(io_err) => matches!(
85 io_err.kind(),
86 std::io::ErrorKind::ConnectionReset
87 | std::io::ErrorKind::ConnectionRefused
88 | std::io::ErrorKind::ConnectionAborted
89 | std::io::ErrorKind::BrokenPipe
90 | std::io::ErrorKind::NotConnected
91 | std::io::ErrorKind::UnexpectedEof
92 ),
93 VoltError::ConnectionNotAvailable => true,
94 VoltError::ConnectionClosed => true,
95 VoltError::Timeout => true,
96 VoltError::RecvError(_) => true,
97 VoltError::PoolShutdown => true,
98 _ => false,
99 }
100 }
101
102 pub fn message_too_large(size: usize) -> Self {
103 VoltError::MessageTooLarge(size)
104 }
105
106 pub fn connection_closed() -> Self {
107 VoltError::ConnectionClosed
108 }
109
110 pub fn timeout() -> Self {
111 VoltError::Timeout
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::io;
119
120 #[test]
121 fn test_volt_error_display() {
122 let err = VoltError::AuthFailed;
123 assert_eq!(format!("{}", err), "Auth failed");
124
125 let err = VoltError::ConnectionNotAvailable;
126 assert_eq!(format!("{}", err), "Connection lost");
127
128 let err = VoltError::Timeout;
129 assert_eq!(format!("{}", err), "Operation timeout");
130 }
131
132 #[test]
133 fn test_volt_error_from_io() {
134 let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "refused");
135 let volt_err: VoltError = io_err.into();
136 assert!(matches!(volt_err, VoltError::Io(_)));
137 }
138
139 #[test]
140 fn test_volt_error_unexpected_null() {
141 let err = VoltError::UnexpectedNull("my_column".to_string());
142 let msg = format!("{}", err);
143 assert!(msg.contains("my_column"));
144 assert!(msg.contains("Option<T>"));
145 }
146
147 #[test]
148 fn test_pool_shutdown_display() {
149 assert_eq!(
150 format!("{}", VoltError::PoolShutdown),
151 "Pool is shutting down"
152 );
153 }
154
155 #[test]
156 fn test_circuit_open_display() {
157 assert_eq!(
158 format!("{}", VoltError::CircuitOpen),
159 "Circuit breaker is open"
160 );
161 }
162
163 #[test]
164 fn test_pool_exhausted_display() {
165 assert_eq!(
166 format!("{}", VoltError::PoolExhausted),
167 "Pool exhausted, no healthy connections"
168 );
169 }
170
171 #[test]
172 fn test_is_connection_fatal_pool_shutdown() {
173 let err = VoltError::PoolShutdown;
174 assert!(err.is_connection_fatal());
175 }
176
177 #[test]
178 fn test_is_connection_fatal_io_connection_reset() {
179 let err = VoltError::Io(io::Error::new(io::ErrorKind::ConnectionReset, "reset"));
180 assert!(err.is_connection_fatal());
181 }
182
183 #[test]
184 fn test_is_connection_fatal_no_value_not_fatal() {
185 let err = VoltError::NoValue("test".to_string());
186 assert!(!err.is_connection_fatal());
187 }
188
189 #[test]
190 fn test_convenience_constructors() {
191 assert!(matches!(
192 VoltError::message_too_large(100),
193 VoltError::MessageTooLarge(100)
194 ));
195 assert!(matches!(
196 VoltError::connection_closed(),
197 VoltError::ConnectionClosed
198 ));
199 assert!(matches!(VoltError::timeout(), VoltError::Timeout));
200 }
201}