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("connection not ready")]
91 ConnectionNotReady,
92
93 #[error("cursor is closed")]
95 CursorClosed,
96
97 #[error("invalid cursor: {0}")]
99 InvalidCursor(String),
100
101 #[error("connection timeout after {0:?}")]
103 ConnectionTimeout(std::time::Duration),
104
105 #[error("invalid connection string: {0}")]
107 InvalidConnectionString(String),
108
109 #[error("invalid service name{}: {}",
111 service_name.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
112 message.as_deref().unwrap_or("service not found"))]
113 InvalidServiceName {
114 service_name: Option<String>,
115 message: Option<String>,
116 },
117
118 #[error("invalid SID{}: {}",
120 sid.as_ref().map(|s| format!(": {}", s)).unwrap_or_default(),
121 message.as_deref().unwrap_or("SID not found"))]
122 InvalidSid {
123 sid: Option<String>,
124 message: Option<String>,
125 },
126
127 #[error("authentication failed: {0}")]
132 AuthenticationFailed(String),
133
134 #[error("invalid username or password")]
136 InvalidCredentials,
137
138 #[error("unsupported verifier type: {0:#x}")]
140 UnsupportedVerifierType(u32),
141
142 #[error("ORA-{code:05}: {message}")]
147 OracleError { code: u32, message: String },
148
149 #[error("SQL execution error: {0}")]
151 SqlError(String),
152
153 #[error("no data found")]
155 NoDataFound,
156
157 #[error("server error ({code}): {message}")]
159 ServerError { code: u32, message: String },
160
161 #[error("invalid data type: {0}")]
166 InvalidDataType(u16),
167
168 #[error("invalid Oracle type: {0}")]
170 InvalidOracleType(u8),
171
172 #[error("data conversion error: {0}")]
174 DataConversionError(String),
175
176 #[error("unexpected NULL value")]
178 UnexpectedNull,
179
180 #[error("I/O error: {0}")]
185 Io(#[from] io::Error),
186
187 #[error("feature not supported: {0}")]
192 FeatureNotSupported(String),
193
194 #[error("native network encryption and data integrity is required but not supported")]
196 NativeNetworkEncryptionRequired,
197
198 #[error("internal error: {0}")]
203 Internal(String),
204}
205
206impl Error {
207 pub fn oracle(code: u32, message: impl Into<String>) -> Self {
209 Error::OracleError {
210 code,
211 message: message.into(),
212 }
213 }
214
215 pub fn is_no_data_found(&self) -> bool {
217 matches!(self, Error::NoDataFound)
218 || matches!(self, Error::OracleError { code, .. } if *code == crate::constants::error_code::NO_DATA_FOUND)
219 }
220
221 pub fn is_connection_error(&self) -> bool {
223 matches!(
224 self,
225 Error::ConnectionRefused { .. }
226 | Error::ConnectionClosed
227 | Error::ConnectionTimeout(_)
228 | Error::Io(_)
229 )
230 }
231
232 pub fn is_recoverable(&self) -> bool {
234 matches!(self, Error::ConnectionTimeout(_) | Error::ConnectionClosed)
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_oracle_error_display() {
244 let err = Error::oracle(1017, "invalid username/password");
245 assert_eq!(
246 err.to_string(),
247 "ORA-01017: invalid username/password"
248 );
249 }
250
251 #[test]
252 fn test_is_no_data_found() {
253 assert!(Error::NoDataFound.is_no_data_found());
254 assert!(Error::oracle(1403, "no data found").is_no_data_found());
255 assert!(!Error::oracle(1017, "test").is_no_data_found());
256 }
257
258 #[test]
259 fn test_is_connection_error() {
260 assert!(Error::ConnectionClosed.is_connection_error());
261 assert!(Error::ConnectionRefused {
262 error_code: Some(12514),
263 message: Some("test".to_string()),
264 }
265 .is_connection_error());
266 assert!(!Error::NoDataFound.is_connection_error());
267 }
268}