1use std::io;
7use thiserror::Error;
8
9pub type Result<T> = std::result::Result<T, Error>;
11
12#[derive(Error, Debug)]
14#[allow(missing_docs)]
15pub enum Error {
16 #[error("invalid packet type: {0}")]
21 InvalidPacketType(u8),
22
23 #[error("invalid message type: {0}")]
25 InvalidMessageType(u8),
26
27 #[error("packet too short: expected at least {expected} bytes, got {actual}")]
29 PacketTooShort { expected: usize, actual: usize },
30
31 #[error("unexpected packet type: expected {expected:?}, got {actual:?}")]
33 UnexpectedPacketType {
34 expected: crate::constants::PacketType,
35 actual: crate::constants::PacketType,
36 },
37
38 #[error("server protocol version {0} not supported (minimum: {1})")]
40 ProtocolVersionNotSupported(u16, u16),
41
42 #[error("protocol error: {0}")]
44 Protocol(String),
45
46 #[error("protocol error: {0}")]
48 ProtocolError(String),
49
50 #[error("buffer underflow: need {needed} bytes but only {available} available")]
55 BufferUnderflow { needed: usize, available: usize },
56
57 #[error("buffer overflow: need {needed} bytes but only {available} available")]
59 BufferOverflow { needed: usize, available: usize },
60
61 #[error("invalid length indicator: {0}")]
63 InvalidLengthIndicator(u8),
64
65 #[error("connection refused{}: {}",
70 error_code.map(|c| format!(" (error {})", c)).unwrap_or_default(),
71 message.as_deref().unwrap_or("unknown reason"))]
72 ConnectionRefused {
73 error_code: Option<u32>,
74 message: Option<String>,
75 },
76
77 #[error("connection redirected to: {address}")]
79 ConnectionRedirected { address: String },
80
81 #[error("connection redirected to: {0}")]
83 ConnectionRedirect(String),
84
85 #[error("connection closed unexpectedly")]
87 ConnectionClosed,
88
89 #[error("{0}")]
91 ConnectionClosedByServer(String),
92
93 #[error("connection not ready")]
95 ConnectionNotReady,
96
97 #[error("cursor is closed")]
99 CursorClosed,
100
101 #[error("invalid cursor: {0}")]
103 InvalidCursor(String),
104
105 #[error("connection timeout after {0:?}")]
107 ConnectionTimeout(std::time::Duration),
108
109 #[error("invalid connection string: {0}")]
111 InvalidConnectionString(String),
112
113 #[error("invalid service name{}: {}",
115 service_name.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
116 message.as_deref().unwrap_or("service not found"))]
117 InvalidServiceName {
118 service_name: Option<String>,
119 message: Option<String>,
120 },
121
122 #[error("invalid SID{}: {}",
124 sid.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
125 message.as_deref().unwrap_or("SID not found"))]
126 InvalidSid {
127 sid: Option<String>,
128 message: Option<String>,
129 },
130
131 #[error("authentication failed: {0}")]
136 AuthenticationFailed(String),
137
138 #[error("invalid username or password")]
140 InvalidCredentials,
141
142 #[error("unsupported verifier type: {0:#x}")]
144 UnsupportedVerifierType(u32),
145
146 #[error("ORA-{code:05}: {message}")]
151 OracleError { code: u32, message: String },
152
153 #[error("SQL execution error: {0}")]
155 SqlError(String),
156
157 #[error("no data found")]
159 NoDataFound,
160
161 #[error("server error ({code}): {message}")]
163 ServerError { code: u32, message: String },
164
165 #[error("invalid data type: {0}")]
170 InvalidDataType(u16),
171
172 #[error("invalid Oracle type: {0}")]
174 InvalidOracleType(u8),
175
176 #[error("data conversion error: {0}")]
178 DataConversionError(String),
179
180 #[error("unexpected NULL value")]
182 UnexpectedNull,
183
184 #[error("I/O error: {0}")]
189 Io(#[from] io::Error),
190
191 #[error("feature not supported: {0}")]
196 FeatureNotSupported(String),
197
198 #[error("native network encryption and data integrity is required but not supported")]
200 NativeNetworkEncryptionRequired,
201
202 #[error("internal error: {0}")]
207 Internal(String),
208}
209
210impl Error {
211 pub fn oracle(code: u32, message: impl Into<String>) -> Self {
213 Error::OracleError {
214 code,
215 message: message.into(),
216 }
217 }
218
219 pub fn is_no_data_found(&self) -> bool {
221 matches!(self, Error::NoDataFound)
222 || matches!(self, Error::OracleError { code, .. } if *code == crate::constants::error_code::NO_DATA_FOUND)
223 }
224
225 pub fn is_connection_error(&self) -> bool {
227 matches!(
228 self,
229 Error::ConnectionRefused { .. }
230 | Error::ConnectionClosed
231 | Error::ConnectionClosedByServer(_)
232 | Error::ConnectionTimeout(_)
233 | Error::Io(_)
234 )
235 }
236
237 pub fn is_recoverable(&self) -> bool {
239 matches!(self, Error::ConnectionTimeout(_) | Error::ConnectionClosed)
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_oracle_error_display() {
249 let err = Error::oracle(1017, "invalid username/password");
250 assert_eq!(
251 err.to_string(),
252 "ORA-01017: invalid username/password"
253 );
254 }
255
256 #[test]
257 fn test_is_no_data_found() {
258 assert!(Error::NoDataFound.is_no_data_found());
259 assert!(Error::oracle(1403, "no data found").is_no_data_found());
260 assert!(!Error::oracle(1017, "test").is_no_data_found());
261 }
262
263 #[test]
264 fn test_is_connection_error() {
265 assert!(Error::ConnectionClosed.is_connection_error());
266 assert!(Error::ConnectionRefused {
267 error_code: Some(12514),
268 message: Some("test".to_string()),
269 }
270 .is_connection_error());
271 assert!(!Error::NoDataFound.is_connection_error());
272 }
273}