1pub use crate::tds::codec::TokenError;
3pub use std::io::ErrorKind as IoErrorKind;
4use std::{borrow::Cow, convert::Infallible, io};
5use thiserror::Error;
6
7#[derive(Debug, Clone, Error, PartialEq, Eq)]
10pub enum Error {
11 #[error("An error occurred during the attempt of performing I/O: {}", message)]
12 Io {
14 kind: IoErrorKind,
16 message: String,
18 },
19 #[error("Protocol error: {}", _0)]
20 Protocol(Cow<'static, str>),
22 #[error("Encoding error: {}", _0)]
23 Encoding(Cow<'static, str>),
25 #[error("Conversion error: {}", _0)]
26 Conversion(Cow<'static, str>),
28 #[error("UTF-8 error")]
29 Utf8,
31 #[error("UTF-16 error")]
32 Utf16,
34 #[error("Error parsing an integer: {}", _0)]
35 ParseInt(std::num::ParseIntError),
37 #[error("Token error: {}", _0)]
38 Server(TokenError),
42 #[error("Error forming TLS connection: {}", _0)]
43 Tls(String),
45 #[cfg(any(all(unix, feature = "integrated-auth-gssapi"), doc))]
46 #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "integrated-auth-gssapi"))))]
47 #[error("GSSAPI Error: {}", _0)]
49 Gssapi(String),
50 #[cfg(any(all(unix, feature = "sspi-rs"), doc))]
51 #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "sspi-rs"))))]
52 #[error("sspi-rs Error: {}", _0)]
54 SspiRs(String),
55 #[error(
56 "Server requested a connection to an alternative address: `{}:{}`",
57 host,
58 port
59 )]
60 Routing {
62 host: String,
64 port: u16,
66 },
67 #[error("BULK UPLOAD input failure: {0}")]
68 BulkInput(Cow<'static, str>),
70}
71
72impl Error {
73 pub fn is_deadlock(&self) -> bool {
75 self.code().map(|c| c == 1205).unwrap_or(false)
76 }
77
78 pub fn code(&self) -> Option<u32> {
81 match self {
82 Error::Server(e) => Some(e.code()),
83 _ => None,
84 }
85 }
86}
87
88impl From<uuid::Error> for Error {
89 fn from(e: uuid::Error) -> Self {
90 Self::Conversion(format!("Error converting a Guid value {}", e).into())
91 }
92}
93
94#[cfg(feature = "native-tls")]
95impl From<async_native_tls::Error> for Error {
96 fn from(v: async_native_tls::Error) -> Self {
97 Error::Tls(format!("{}", v))
98 }
99}
100
101#[cfg(feature = "vendored-openssl")]
102impl From<opentls::Error> for Error {
103 fn from(v: opentls::Error) -> Self {
104 Error::Tls(format!("{}", v))
105 }
106}
107
108impl From<Infallible> for Error {
109 fn from(_: Infallible) -> Self {
110 unreachable!()
111 }
112}
113
114impl From<io::Error> for Error {
115 fn from(err: io::Error) -> Error {
116 if let Some(inner) = err.get_ref().and_then(|e| e.downcast_ref::<Error>()) {
119 return inner.clone();
120 }
121
122 Self::Io {
123 kind: err.kind(),
124 message: format!("{}", err),
125 }
126 }
127}
128
129impl From<std::num::ParseIntError> for Error {
130 fn from(err: std::num::ParseIntError) -> Error {
131 Error::ParseInt(err)
132 }
133}
134
135impl From<std::str::Utf8Error> for Error {
136 fn from(_: std::str::Utf8Error) -> Error {
137 Error::Utf8
138 }
139}
140
141impl From<std::string::FromUtf8Error> for Error {
142 fn from(_err: std::string::FromUtf8Error) -> Error {
143 Error::Utf8
144 }
145}
146
147impl From<std::string::FromUtf16Error> for Error {
148 fn from(_err: std::string::FromUtf16Error) -> Error {
149 Error::Utf16
150 }
151}
152
153impl From<connection_string::Error> for Error {
154 fn from(err: connection_string::Error) -> Error {
155 let err = Cow::Owned(format!("{}", err));
156 Error::Conversion(err)
157 }
158}
159
160#[cfg(all(unix, feature = "integrated-auth-gssapi"))]
161#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "integrated-auth-gssapi"))))]
162impl From<libgssapi::error::Error> for Error {
163 fn from(err: libgssapi::error::Error) -> Error {
164 Error::Gssapi(format!("{}", err))
165 }
166}
167
168#[cfg(all(unix, feature = "sspi-rs"))]
169#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "sspi-rs"))))]
170impl From<sspi::Error> for Error {
171 fn from(err: sspi::Error) -> Error {
172 Error::SspiRs(format!("{}", err))
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 fn token_error(code: u32) -> TokenError {
181 TokenError {
182 code,
183 state: 1,
184 class: 16,
185 message: "boom".to_string(),
186 server: "srv".into(),
187 procedure: "proc".into(),
188 line: 3,
189 related: None,
190 }
191 }
192
193 #[test]
194 fn code_and_is_deadlock() {
195 let deadlock = Error::Server(token_error(1205));
196 assert_eq!(deadlock.code(), Some(1205));
197 assert!(deadlock.is_deadlock());
198
199 let other = Error::Server(token_error(500));
200 assert_eq!(other.code(), Some(500));
201 assert!(!other.is_deadlock());
202
203 let non_server = Error::Utf8;
204 assert_eq!(non_server.code(), None);
205 assert!(!non_server.is_deadlock());
206 }
207
208 #[test]
209 fn display_variants() {
210 assert_eq!(
211 format!("{}", Error::Protocol("bad".into())),
212 "Protocol error: bad"
213 );
214 assert_eq!(
215 format!("{}", Error::Encoding("bad".into())),
216 "Encoding error: bad"
217 );
218 assert_eq!(
219 format!("{}", Error::Conversion("bad".into())),
220 "Conversion error: bad"
221 );
222 assert_eq!(format!("{}", Error::Utf8), "UTF-8 error");
223 assert_eq!(format!("{}", Error::Utf16), "UTF-16 error");
224 assert_eq!(
225 format!("{}", Error::BulkInput("bad".into())),
226 "BULK UPLOAD input failure: bad"
227 );
228
229 let routing = Error::Routing {
230 host: "host".to_string(),
231 port: 1234,
232 };
233 assert!(format!("{}", routing).contains("host:1234"));
234 }
235
236 #[test]
237 fn from_io_error() {
238 let io_err = io::Error::new(io::ErrorKind::UnexpectedEof, "eof");
239 let err: Error = io_err.into();
240 match err {
241 Error::Io { kind, message } => {
242 assert_eq!(kind, io::ErrorKind::UnexpectedEof);
243 assert!(message.contains("eof"));
244 }
245 _ => panic!("expected Io"),
246 }
247 }
248
249 #[test]
250 fn an_error_carried_through_an_io_error_comes_back_as_itself() {
251 let ours = Error::Io {
252 kind: io::ErrorKind::ConnectionReset,
253 message: "reset by peer".into(),
254 };
255 let io_err = io::Error::new(io::ErrorKind::BrokenPipe, ours.clone());
256 assert_eq!(Error::from(io_err), ours);
257
258 let ours = Error::Protocol("bad token".into());
259 let io_err = io::Error::other(ours.clone());
260 assert_eq!(Error::from(io_err), ours);
261 }
262
263 #[test]
264 fn from_parse_int_error() {
265 let parse_err = "not-a-number".parse::<i32>().unwrap_err();
266 let err: Error = parse_err.into();
267 assert!(matches!(err, Error::ParseInt(_)));
268 }
269
270 #[test]
271 #[allow(invalid_from_utf8)] fn from_utf8_and_utf16_errors() {
273 let utf8_err = String::from_utf8(vec![0xff, 0xfe]).unwrap_err();
274 assert!(matches!(Error::from(utf8_err), Error::Utf8));
275
276 let invalid: &[u8] = &[0xff, 0xfe];
277 let str_utf8 = std::str::from_utf8(invalid).unwrap_err();
278 assert!(matches!(Error::from(str_utf8), Error::Utf8));
279
280 let utf16_err = String::from_utf16(&[0xd800]).unwrap_err();
281 assert!(matches!(Error::from(utf16_err), Error::Utf16));
282 }
283
284 #[test]
285 fn from_uuid_error() {
286 let uuid_err = uuid::Uuid::parse_str("not-a-uuid").unwrap_err();
287 assert!(matches!(Error::from(uuid_err), Error::Conversion(_)));
288 }
289
290 #[test]
291 fn equality_between_errors() {
292 assert_eq!(Error::Utf8, Error::Utf8);
293 assert_ne!(Error::Utf8, Error::Utf16);
294 }
295
296 #[test]
297 fn from_connection_string_error() {
298 let cs_err = connection_string::Error::new("bad connection string");
299 let err: Error = cs_err.into();
300 match err {
301 Error::Conversion(msg) => assert!(msg.contains("bad connection string")),
302 _ => panic!("expected Conversion"),
303 }
304 }
305
306 #[cfg(all(unix, feature = "sspi-rs"))]
307 #[test]
308 fn from_sspi_error() {
309 let sspi_err = sspi::Error::new(sspi::ErrorKind::InternalError, "sspi boom");
310 assert!(matches!(Error::from(sspi_err), Error::SspiRs(_)));
311 }
312
313 #[cfg(all(unix, feature = "integrated-auth-gssapi"))]
314 #[test]
315 fn from_gssapi_error() {
316 let gss_err = libgssapi::error::Error {
317 major: libgssapi::error::MajorFlags::empty(),
318 minor: 0,
319 };
320 assert!(matches!(Error::from(gss_err), Error::Gssapi(_)));
321 }
322}