1use std::sync::Arc;
8
9use crate::types::ResponseCode;
10
11#[non_exhaustive]
18#[derive(Debug, Clone, thiserror::Error)]
19pub enum Error {
20 #[error("I/O error: {0}")]
24 Io(#[source] Arc<std::io::Error>),
25
26 #[error("authentication failed: {text}")]
32 Auth {
33 text: String,
35 code: Option<ResponseCode>,
37 },
38
39 #[error("server rejected command: {text}")]
45 No {
46 text: String,
48 code: Option<ResponseCode>,
50 },
51
52 #[error("server reported bad command: {text}")]
57 Bad {
58 text: String,
60 code: Option<ResponseCode>,
62 },
63
64 #[error("server closing connection: {text}")]
72 Bye {
73 text: String,
75 code: Option<ResponseCode>,
77 },
78
79 #[error("protocol error: {0}")]
81 Protocol(String),
82
83 #[error("parse error: {0}")]
85 Parse(String),
86
87 #[error("operation timed out")]
93 Timeout,
94
95 #[error("connection closed")]
97 Closed,
98
99 #[error("STARTTLS not supported by server")]
102 StartTlsUnavailable,
103
104 #[error("missing required capability: {0}")]
107 MissingCapability(String),
108
109 #[error("message size {size} exceeds server APPENDLIMIT of {limit}")]
111 AppendLimit {
112 size: u64,
114 limit: u64,
116 },
117
118 #[error("invalid APPEND date-time: {0}")]
130 InvalidAppendDate(String),
131
132 #[error("internal error: {0}")]
136 Internal(String),
137
138 #[error("driver task panicked: {0}")]
142 DriverPanicked(String),
143
144 #[error("driver task gone")]
147 DriverGone,
148}
149
150impl From<std::io::Error> for Error {
151 fn from(e: std::io::Error) -> Self {
152 Self::Io(Arc::new(e))
153 }
154}
155
156impl From<crate::types::ValidationError> for Error {
157 fn from(e: crate::types::ValidationError) -> Self {
158 Self::Protocol(e.to_string())
159 }
160}
161
162impl From<crate::codec::encode::EncodeError> for Error {
163 fn from(e: crate::codec::encode::EncodeError) -> Self {
164 match e {
165 crate::codec::encode::EncodeError::MissingCapability { cmd, cap } => {
166 Self::MissingCapability(format!("{cmd} requires {cap}"))
167 }
168 crate::codec::encode::EncodeError::Validation(msg) => Self::Protocol(msg),
169 }
170 }
171}
172
173impl PartialEq for Error {
179 fn eq(&self, other: &Self) -> bool {
180 match (self, other) {
181 (Self::Io(a), Self::Io(b)) => a.kind() == b.kind(),
182 (Self::Auth { text: t1, code: c1 }, Self::Auth { text: t2, code: c2 })
183 | (Self::No { text: t1, code: c1 }, Self::No { text: t2, code: c2 })
184 | (Self::Bad { text: t1, code: c1 }, Self::Bad { text: t2, code: c2 })
185 | (Self::Bye { text: t1, code: c1 }, Self::Bye { text: t2, code: c2 }) => {
186 t1 == t2 && c1 == c2
187 }
188 (Self::Protocol(a), Self::Protocol(b))
189 | (Self::Parse(a), Self::Parse(b))
190 | (Self::MissingCapability(a), Self::MissingCapability(b))
191 | (Self::InvalidAppendDate(a), Self::InvalidAppendDate(b))
192 | (Self::Internal(a), Self::Internal(b))
193 | (Self::DriverPanicked(a), Self::DriverPanicked(b)) => a == b,
194 (Self::Timeout, Self::Timeout)
195 | (Self::Closed, Self::Closed)
196 | (Self::StartTlsUnavailable, Self::StartTlsUnavailable)
197 | (Self::DriverGone, Self::DriverGone) => true,
198 (
199 Self::AppendLimit {
200 size: s1,
201 limit: l1,
202 },
203 Self::AppendLimit {
204 size: s2,
205 limit: l2,
206 },
207 ) => s1 == s2 && l1 == l2,
208 _ => false,
209 }
210 }
211}
212
213impl Eq for Error {}
214
215impl Error {
216 pub(crate) fn no_with_code(text: String, code: Option<ResponseCode>) -> Self {
218 Self::No { text, code }
219 }
220
221 pub(crate) fn bad_with_code(text: String, code: Option<ResponseCode>) -> Self {
223 Self::Bad { text, code }
224 }
225
226 pub(crate) fn auth_with_code(text: String, code: Option<ResponseCode>) -> Self {
228 Self::Auth { text, code }
229 }
230
231 pub(crate) fn bye_with_code(text: String, code: Option<ResponseCode>) -> Self {
234 Self::Bye { text, code }
235 }
236}
237
238#[cfg(feature = "serde")]
243mod serde_support {
244 use super::{Arc, Error, ResponseCode};
245 use serde::{Deserialize, Deserializer, Serialize, Serializer};
246
247 fn error_kind_to_str(kind: std::io::ErrorKind) -> &'static str {
250 match kind {
251 std::io::ErrorKind::NotFound => "NotFound",
252 std::io::ErrorKind::PermissionDenied => "PermissionDenied",
253 std::io::ErrorKind::ConnectionRefused => "ConnectionRefused",
254 std::io::ErrorKind::ConnectionReset => "ConnectionReset",
255 std::io::ErrorKind::ConnectionAborted => "ConnectionAborted",
256 std::io::ErrorKind::NotConnected => "NotConnected",
257 std::io::ErrorKind::AddrInUse => "AddrInUse",
258 std::io::ErrorKind::AddrNotAvailable => "AddrNotAvailable",
259 std::io::ErrorKind::BrokenPipe => "BrokenPipe",
260 std::io::ErrorKind::AlreadyExists => "AlreadyExists",
261 std::io::ErrorKind::WouldBlock => "WouldBlock",
262 std::io::ErrorKind::InvalidInput => "InvalidInput",
263 std::io::ErrorKind::InvalidData => "InvalidData",
264 std::io::ErrorKind::TimedOut => "TimedOut",
265 std::io::ErrorKind::WriteZero => "WriteZero",
266 std::io::ErrorKind::Interrupted => "Interrupted",
267 std::io::ErrorKind::Unsupported => "Unsupported",
268 std::io::ErrorKind::UnexpectedEof => "UnexpectedEof",
269 std::io::ErrorKind::OutOfMemory => "OutOfMemory",
270 _ => "Other",
271 }
272 }
273
274 fn error_kind_from_str(s: &str) -> std::io::ErrorKind {
277 match s {
278 "NotFound" => std::io::ErrorKind::NotFound,
279 "PermissionDenied" => std::io::ErrorKind::PermissionDenied,
280 "ConnectionRefused" => std::io::ErrorKind::ConnectionRefused,
281 "ConnectionReset" => std::io::ErrorKind::ConnectionReset,
282 "ConnectionAborted" => std::io::ErrorKind::ConnectionAborted,
283 "NotConnected" => std::io::ErrorKind::NotConnected,
284 "AddrInUse" => std::io::ErrorKind::AddrInUse,
285 "AddrNotAvailable" => std::io::ErrorKind::AddrNotAvailable,
286 "BrokenPipe" => std::io::ErrorKind::BrokenPipe,
287 "AlreadyExists" => std::io::ErrorKind::AlreadyExists,
288 "WouldBlock" => std::io::ErrorKind::WouldBlock,
289 "InvalidInput" => std::io::ErrorKind::InvalidInput,
290 "InvalidData" => std::io::ErrorKind::InvalidData,
291 "TimedOut" => std::io::ErrorKind::TimedOut,
292 "WriteZero" => std::io::ErrorKind::WriteZero,
293 "Interrupted" => std::io::ErrorKind::Interrupted,
294 "Unsupported" => std::io::ErrorKind::Unsupported,
295 "UnexpectedEof" => std::io::ErrorKind::UnexpectedEof,
296 "OutOfMemory" => std::io::ErrorKind::OutOfMemory,
297 _ => std::io::ErrorKind::Other,
298 }
299 }
300
301 #[derive(Serialize, Deserialize)]
303 struct IoFields {
304 kind: String,
305 message: String,
306 }
307
308 #[derive(Serialize, Deserialize)]
313 #[serde(tag = "type", content = "data")]
314 enum ErrorRepr {
315 Io(IoFields),
316 Auth {
317 text: String,
318 code: Option<ResponseCode>,
319 },
320 No {
321 text: String,
322 code: Option<ResponseCode>,
323 },
324 Bad {
325 text: String,
326 code: Option<ResponseCode>,
327 },
328 Bye {
329 text: String,
330 code: Option<ResponseCode>,
331 },
332 Protocol {
333 message: String,
334 },
335 Parse {
336 message: String,
337 },
338 Timeout,
339 Closed,
340 StartTlsUnavailable,
341 MissingCapability {
342 capability: String,
343 },
344 AppendLimit {
345 size: u64,
346 limit: u64,
347 },
348 InvalidAppendDate {
349 date: String,
350 },
351 Internal {
352 message: String,
353 },
354 DriverPanicked {
355 message: String,
356 },
357 DriverGone,
358 }
359
360 impl From<&Error> for ErrorRepr {
361 fn from(err: &Error) -> Self {
362 match err {
363 Error::Io(e) => Self::Io(IoFields {
364 kind: error_kind_to_str(e.kind()).to_owned(),
365 message: e.to_string(),
366 }),
367 Error::Auth { text, code } => Self::Auth {
368 text: text.clone(),
369 code: code.clone(),
370 },
371 Error::No { text, code } => Self::No {
372 text: text.clone(),
373 code: code.clone(),
374 },
375 Error::Bad { text, code } => Self::Bad {
376 text: text.clone(),
377 code: code.clone(),
378 },
379 Error::Bye { text, code } => Self::Bye {
380 text: text.clone(),
381 code: code.clone(),
382 },
383 Error::Protocol(msg) => Self::Protocol {
384 message: msg.clone(),
385 },
386 Error::Parse(msg) => Self::Parse {
387 message: msg.clone(),
388 },
389 Error::Timeout => Self::Timeout,
390 Error::Closed => Self::Closed,
391 Error::StartTlsUnavailable => Self::StartTlsUnavailable,
392 Error::MissingCapability(cap) => Self::MissingCapability {
393 capability: cap.clone(),
394 },
395 Error::AppendLimit { size, limit } => Self::AppendLimit {
396 size: *size,
397 limit: *limit,
398 },
399 Error::InvalidAppendDate(msg) => Self::InvalidAppendDate { date: msg.clone() },
400 Error::Internal(msg) => Self::Internal {
401 message: msg.clone(),
402 },
403 Error::DriverPanicked(msg) => Self::DriverPanicked {
404 message: msg.clone(),
405 },
406 Error::DriverGone => Self::DriverGone,
407 }
408 }
409 }
410
411 impl From<ErrorRepr> for Error {
412 fn from(repr: ErrorRepr) -> Self {
413 match repr {
414 ErrorRepr::Io(fields) => {
415 let kind = error_kind_from_str(&fields.kind);
416 Self::Io(Arc::new(std::io::Error::new(kind, fields.message)))
417 }
418 ErrorRepr::Auth { text, code } => Self::Auth { text, code },
419 ErrorRepr::No { text, code } => Self::No { text, code },
420 ErrorRepr::Bad { text, code } => Self::Bad { text, code },
421 ErrorRepr::Bye { text, code } => Self::Bye { text, code },
422 ErrorRepr::Protocol { message } => Self::Protocol(message),
423 ErrorRepr::Parse { message } => Self::Parse(message),
424 ErrorRepr::Timeout => Self::Timeout,
425 ErrorRepr::Closed => Self::Closed,
426 ErrorRepr::StartTlsUnavailable => Self::StartTlsUnavailable,
427 ErrorRepr::MissingCapability { capability } => Self::MissingCapability(capability),
428 ErrorRepr::AppendLimit { size, limit } => Self::AppendLimit { size, limit },
429 ErrorRepr::InvalidAppendDate { date } => Self::InvalidAppendDate(date),
430 ErrorRepr::Internal { message } => Self::Internal(message),
431 ErrorRepr::DriverPanicked { message } => Self::DriverPanicked(message),
432 ErrorRepr::DriverGone => Self::DriverGone,
433 }
434 }
435 }
436
437 impl Serialize for Error {
438 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
439 ErrorRepr::from(self).serialize(serializer)
440 }
441 }
442
443 impl<'de> Deserialize<'de> for Error {
444 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
445 ErrorRepr::deserialize(deserializer).map(Self::from)
446 }
447 }
448}
449
450#[cfg(test)]
451#[path = "error_tests.rs"]
452mod tests;