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),
40 #[error("Error forming TLS connection: {}", _0)]
41 Tls(String),
43 #[cfg(any(all(unix, feature = "integrated-auth-gssapi"), doc))]
44 #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "integrated-auth-gssapi"))))]
45 #[error("GSSAPI Error: {}", _0)]
47 Gssapi(String),
48 #[cfg(any(all(unix, feature = "sspi-rs"), doc))]
49 #[cfg_attr(docsrs, doc(cfg(all(unix, feature = "sspi-rs"))))]
50 #[error("sspi-rs Error: {}", _0)]
52 SspiRs(String),
53 #[error(
54 "Server requested a connection to an alternative address: `{}:{}`",
55 host,
56 port
57 )]
58 Routing {
60 host: String,
62 port: u16,
64 },
65 #[error("BULK UPLOAD input failure: {0}")]
66 BulkInput(Cow<'static, str>),
68}
69
70impl Error {
71 pub fn is_deadlock(&self) -> bool {
73 self.code().map(|c| c == 1205).unwrap_or(false)
74 }
75
76 pub fn code(&self) -> Option<u32> {
79 match self {
80 Error::Server(e) => Some(e.code()),
81 _ => None,
82 }
83 }
84}
85
86impl From<uuid::Error> for Error {
87 fn from(e: uuid::Error) -> Self {
88 Self::Conversion(format!("Error converting a Guid value {}", e).into())
89 }
90}
91
92#[cfg(feature = "native-tls")]
93impl From<async_native_tls::Error> for Error {
94 fn from(v: async_native_tls::Error) -> Self {
95 Error::Tls(format!("{}", v))
96 }
97}
98
99#[cfg(feature = "vendored-openssl")]
100impl From<opentls::Error> for Error {
101 fn from(v: opentls::Error) -> Self {
102 Error::Tls(format!("{}", v))
103 }
104}
105
106impl From<Infallible> for Error {
107 fn from(_: Infallible) -> Self {
108 unreachable!()
109 }
110}
111
112impl From<io::Error> for Error {
113 fn from(err: io::Error) -> Error {
114 Self::Io {
115 kind: err.kind(),
116 message: format!("{}", err),
117 }
118 }
119}
120
121impl From<std::num::ParseIntError> for Error {
122 fn from(err: std::num::ParseIntError) -> Error {
123 Error::ParseInt(err)
124 }
125}
126
127impl From<std::str::Utf8Error> for Error {
128 fn from(_: std::str::Utf8Error) -> Error {
129 Error::Utf8
130 }
131}
132
133impl From<std::string::FromUtf8Error> for Error {
134 fn from(_err: std::string::FromUtf8Error) -> Error {
135 Error::Utf8
136 }
137}
138
139impl From<std::string::FromUtf16Error> for Error {
140 fn from(_err: std::string::FromUtf16Error) -> Error {
141 Error::Utf16
142 }
143}
144
145impl From<connection_string::Error> for Error {
146 fn from(err: connection_string::Error) -> Error {
147 let err = Cow::Owned(format!("{}", err));
148 Error::Conversion(err)
149 }
150}
151
152#[cfg(all(unix, feature = "integrated-auth-gssapi"))]
153#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "integrated-auth-gssapi"))))]
154impl From<libgssapi::error::Error> for Error {
155 fn from(err: libgssapi::error::Error) -> Error {
156 Error::Gssapi(format!("{}", err))
157 }
158}
159
160#[cfg(all(unix, feature = "sspi-rs"))]
161#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "sspi-rs"))))]
162impl From<sspi::Error> for Error {
163 fn from(err: sspi::Error) -> Error {
164 Error::SspiRs(format!("{}", err))
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 fn token_error(code: u32) -> TokenError {
173 TokenError {
174 code,
175 state: 1,
176 class: 16,
177 message: "boom".to_string(),
178 server: "srv".to_string(),
179 procedure: "proc".to_string(),
180 line: 3,
181 }
182 }
183
184 #[test]
185 fn code_and_is_deadlock() {
186 let deadlock = Error::Server(token_error(1205));
187 assert_eq!(deadlock.code(), Some(1205));
188 assert!(deadlock.is_deadlock());
189
190 let other = Error::Server(token_error(500));
191 assert_eq!(other.code(), Some(500));
192 assert!(!other.is_deadlock());
193
194 let non_server = Error::Utf8;
195 assert_eq!(non_server.code(), None);
196 assert!(!non_server.is_deadlock());
197 }
198
199 #[test]
200 fn display_variants() {
201 assert_eq!(
202 format!("{}", Error::Protocol("bad".into())),
203 "Protocol error: bad"
204 );
205 assert_eq!(
206 format!("{}", Error::Encoding("bad".into())),
207 "Encoding error: bad"
208 );
209 assert_eq!(
210 format!("{}", Error::Conversion("bad".into())),
211 "Conversion error: bad"
212 );
213 assert_eq!(format!("{}", Error::Utf8), "UTF-8 error");
214 assert_eq!(format!("{}", Error::Utf16), "UTF-16 error");
215 assert_eq!(
216 format!("{}", Error::BulkInput("bad".into())),
217 "BULK UPLOAD input failure: bad"
218 );
219
220 let routing = Error::Routing {
221 host: "host".to_string(),
222 port: 1234,
223 };
224 assert!(format!("{}", routing).contains("host:1234"));
225 }
226
227 #[test]
228 fn from_io_error() {
229 let io_err = io::Error::new(io::ErrorKind::UnexpectedEof, "eof");
230 let err: Error = io_err.into();
231 match err {
232 Error::Io { kind, message } => {
233 assert_eq!(kind, io::ErrorKind::UnexpectedEof);
234 assert!(message.contains("eof"));
235 }
236 _ => panic!("expected Io"),
237 }
238 }
239
240 #[test]
241 fn from_parse_int_error() {
242 let parse_err = "not-a-number".parse::<i32>().unwrap_err();
243 let err: Error = parse_err.into();
244 assert!(matches!(err, Error::ParseInt(_)));
245 }
246
247 #[test]
248 #[allow(invalid_from_utf8)] fn from_utf8_and_utf16_errors() {
250 let utf8_err = String::from_utf8(vec![0xff, 0xfe]).unwrap_err();
251 assert!(matches!(Error::from(utf8_err), Error::Utf8));
252
253 let invalid: &[u8] = &[0xff, 0xfe];
254 let str_utf8 = std::str::from_utf8(invalid).unwrap_err();
255 assert!(matches!(Error::from(str_utf8), Error::Utf8));
256
257 let utf16_err = String::from_utf16(&[0xd800]).unwrap_err();
258 assert!(matches!(Error::from(utf16_err), Error::Utf16));
259 }
260
261 #[test]
262 fn from_uuid_error() {
263 let uuid_err = uuid::Uuid::parse_str("not-a-uuid").unwrap_err();
264 assert!(matches!(Error::from(uuid_err), Error::Conversion(_)));
265 }
266
267 #[test]
268 fn equality_between_errors() {
269 assert_eq!(Error::Utf8, Error::Utf8);
270 assert_ne!(Error::Utf8, Error::Utf16);
271 }
272
273 #[test]
274 fn from_connection_string_error() {
275 let cs_err = connection_string::Error::new("bad connection string");
276 let err: Error = cs_err.into();
277 match err {
278 Error::Conversion(msg) => assert!(msg.contains("bad connection string")),
279 _ => panic!("expected Conversion"),
280 }
281 }
282
283 #[cfg(all(unix, feature = "sspi-rs"))]
284 #[test]
285 fn from_sspi_error() {
286 let sspi_err = sspi::Error::new(sspi::ErrorKind::InternalError, "sspi boom");
287 assert!(matches!(Error::from(sspi_err), Error::SspiRs(_)));
288 }
289
290 #[cfg(all(unix, feature = "integrated-auth-gssapi"))]
291 #[test]
292 fn from_gssapi_error() {
293 let gss_err = libgssapi::error::Error {
294 major: libgssapi::error::MajorFlags::empty(),
295 minor: 0,
296 };
297 assert!(matches!(Error::from(gss_err), Error::Gssapi(_)));
298 }
299}