lettre/transport/smtp/
error.rs1use std::{error::Error as StdError, fmt};
4
5use crate::{
6 transport::smtp::response::{Code, Severity},
7 BoxError,
8};
9
10pub struct Error {
14 inner: Box<Inner>,
15}
16
17struct Inner {
18 kind: Kind,
19 source: Option<BoxError>,
20}
21
22impl Error {
23 pub(crate) fn new<E>(kind: Kind, source: Option<E>) -> Error
24 where
25 E: Into<BoxError>,
26 {
27 Error {
28 inner: Box::new(Inner {
29 kind,
30 source: source.map(Into::into),
31 }),
32 }
33 }
34
35 pub fn is_response(&self) -> bool {
37 matches!(self.inner.kind, Kind::Response)
38 }
39
40 pub fn is_client(&self) -> bool {
42 matches!(self.inner.kind, Kind::Client)
43 }
44
45 pub fn is_transient(&self) -> bool {
47 matches!(self.inner.kind, Kind::Transient(_))
48 }
49
50 pub fn is_permanent(&self) -> bool {
52 matches!(self.inner.kind, Kind::Permanent(_))
53 }
54
55 pub fn is_timeout(&self) -> bool {
57 let mut source = self.source();
58
59 while let Some(err) = source {
60 if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
61 return io_err.kind() == std::io::ErrorKind::TimedOut;
62 }
63
64 source = err.source();
65 }
66
67 false
68 }
69
70 #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
72 #[cfg_attr(
73 docsrs,
74 doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
75 )]
76 pub fn is_tls(&self) -> bool {
77 matches!(self.inner.kind, Kind::Tls)
78 }
79
80 pub fn is_transport_shutdown(&self) -> bool {
82 matches!(self.inner.kind, Kind::TransportShutdown)
83 }
84
85 pub fn status(&self) -> Option<Code> {
87 match self.inner.kind {
88 Kind::Transient(code) | Kind::Permanent(code) => Some(code),
89 _ => None,
90 }
91 }
92}
93
94#[derive(Debug)]
95pub(crate) enum Kind {
96 Transient(Code),
100 Permanent(Code),
104 Response,
106 Client,
108 Connection,
110 Network,
112 #[cfg_attr(
114 docsrs,
115 doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
116 )]
117 #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
118 Tls,
119 TransportShutdown,
121}
122
123impl fmt::Debug for Error {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 let mut builder = f.debug_struct("lettre::transport::smtp::Error");
126
127 builder.field("kind", &self.inner.kind);
128
129 if let Some(source) = &self.inner.source {
130 builder.field("source", source);
131 }
132
133 builder.finish()
134 }
135}
136
137impl fmt::Display for Error {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match &self.inner.kind {
140 Kind::Response => f.write_str("response error")?,
141 Kind::Client => f.write_str("internal client error")?,
142 Kind::Network => f.write_str("network error")?,
143 Kind::Connection => f.write_str("Connection error")?,
144 #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
145 Kind::Tls => f.write_str("tls error")?,
146 Kind::TransportShutdown => f.write_str("transport has been shut down")?,
147 Kind::Transient(code) => {
148 write!(f, "transient error ({code})")?;
149 }
150 Kind::Permanent(code) => {
151 write!(f, "permanent error ({code})")?;
152 }
153 }
154
155 if let Some(e) = &self.inner.source {
156 write!(f, ": {e}")?;
157 }
158
159 Ok(())
160 }
161}
162
163impl StdError for Error {
164 fn source(&self) -> Option<&(dyn StdError + 'static)> {
165 self.inner.source.as_ref().map(|e| {
166 let r: &(dyn std::error::Error + 'static) = &**e;
167 r
168 })
169 }
170}
171
172pub(crate) fn code(c: Code, s: Option<String>) -> Error {
173 match c.severity {
174 Severity::TransientNegativeCompletion => Error::new(Kind::Transient(c), s),
175 Severity::PermanentNegativeCompletion => Error::new(Kind::Permanent(c), s),
176 _ => client("Unknown error code"),
177 }
178}
179
180pub(crate) fn response<E: Into<BoxError>>(e: E) -> Error {
181 Error::new(Kind::Response, Some(e))
182}
183
184pub(crate) fn client<E: Into<BoxError>>(e: E) -> Error {
185 Error::new(Kind::Client, Some(e))
186}
187
188pub(crate) fn network<E: Into<BoxError>>(e: E) -> Error {
189 Error::new(Kind::Network, Some(e))
190}
191
192pub(crate) fn connection<E: Into<BoxError>>(e: E) -> Error {
193 Error::new(Kind::Connection, Some(e))
194}
195
196#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
197pub(crate) fn tls<E: Into<BoxError>>(e: E) -> Error {
198 Error::new(Kind::Tls, Some(e))
199}
200
201pub(crate) fn transport_shutdown() -> Error {
202 Error::new::<BoxError>(Kind::TransportShutdown, None)
203}