feat(transport): Seal file and sendmail error types (#567)
This commit is contained in:
@@ -102,6 +102,9 @@ use crate::{address::Envelope, error::Error};
|
||||
|
||||
#[cfg(feature = "smtp-transport")]
|
||||
pub use crate::transport::smtp::SmtpTransport;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "builder")]
|
||||
|
||||
@@ -1,38 +1,96 @@
|
||||
//! Error and result type for file transport
|
||||
|
||||
use self::Error::*;
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt::{self, Display, Formatter},
|
||||
io,
|
||||
};
|
||||
use crate::BoxError;
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
/// An enum of all error kinds.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// IO error
|
||||
Io(io::Error),
|
||||
/// JSON error
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
Json(serde_json::Error),
|
||||
/// The Errors that may occur when sending an email over SMTP
|
||||
pub struct Error {
|
||||
inner: Box<Inner>,
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match &self {
|
||||
Io(err) => err.fmt(fmt),
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
Json(err) => err.fmt(fmt),
|
||||
struct Inner {
|
||||
kind: Kind,
|
||||
source: Option<BoxError>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn new<E>(kind: Kind, source: Option<E>) -> Error
|
||||
where
|
||||
E: Into<BoxError>,
|
||||
{
|
||||
Error {
|
||||
inner: Box::new(Inner {
|
||||
kind,
|
||||
source: source.map(Into::into),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the error is a file I/O error
|
||||
pub fn is_io(&self) -> bool {
|
||||
matches!(self.inner.kind, Kind::Io)
|
||||
}
|
||||
|
||||
/// Returns true if the error is an envelope serialization or deserialization error
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
pub fn is_envelope(&self) -> bool {
|
||||
matches!(self.inner.kind, Kind::Envelope)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Kind {
|
||||
/// File I/O error
|
||||
Io,
|
||||
/// Envelope serialization/deserialization error
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
Envelope,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut builder = f.debug_struct("lettre::transport::file::Error");
|
||||
|
||||
builder.field("kind", &self.inner.kind);
|
||||
|
||||
if let Some(ref source) = self.inner.source {
|
||||
builder.field("source", source);
|
||||
}
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.inner.kind {
|
||||
Kind::Io => f.write_str("response error")?,
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
Kind::Envelope => f.write_str("internal client error")?,
|
||||
};
|
||||
|
||||
if let Some(ref e) = self.inner.source {
|
||||
write!(f, ": {}", e)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match &self {
|
||||
Io(err) => Some(&*err),
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
Json(err) => Some(&*err),
|
||||
}
|
||||
self.inner.source.as_ref().map(|e| {
|
||||
let r: &(dyn std::error::Error + 'static) = &**e;
|
||||
r
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn io<E: Into<BoxError>>(e: E) -> Error {
|
||||
Error::new(Kind::Io, Some(e))
|
||||
}
|
||||
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
pub(crate) fn envelope<E: Into<BoxError>>(e: E) -> Error {
|
||||
Error::new(Kind::Envelope, Some(e))
|
||||
}
|
||||
|
||||
@@ -206,11 +206,11 @@ impl FileTransport {
|
||||
use std::fs;
|
||||
|
||||
let eml_file = self.path.join(format!("{}.eml", email_id));
|
||||
let eml = fs::read(eml_file).map_err(Error::Io)?;
|
||||
let eml = fs::read(eml_file).map_err(error::io)?;
|
||||
|
||||
let json_file = self.path.join(format!("{}.json", email_id));
|
||||
let json = fs::read(&json_file).map_err(Error::Io)?;
|
||||
let envelope = serde_json::from_slice(&json).map_err(Error::Json)?;
|
||||
let json = fs::read(&json_file).map_err(error::io)?;
|
||||
let envelope = serde_json::from_slice(&json).map_err(error::envelope)?;
|
||||
|
||||
Ok((envelope, eml))
|
||||
}
|
||||
@@ -253,11 +253,11 @@ where
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
pub async fn read(&self, email_id: &str) -> Result<(Envelope, Vec<u8>), Error> {
|
||||
let eml_file = self.inner.path.join(format!("{}.eml", email_id));
|
||||
let eml = E::fs_read(&eml_file).await.map_err(Error::Io)?;
|
||||
let eml = E::fs_read(&eml_file).await.map_err(error::io)?;
|
||||
|
||||
let json_file = self.inner.path.join(format!("{}.json", email_id));
|
||||
let json = E::fs_read(&json_file).await.map_err(Error::Io)?;
|
||||
let envelope = serde_json::from_slice(&json).map_err(Error::Json)?;
|
||||
let json = E::fs_read(&json_file).await.map_err(error::io)?;
|
||||
let envelope = serde_json::from_slice(&json).map_err(error::envelope)?;
|
||||
|
||||
Ok((envelope, eml))
|
||||
}
|
||||
@@ -273,14 +273,14 @@ impl Transport for FileTransport {
|
||||
let email_id = Uuid::new_v4();
|
||||
|
||||
let file = self.path(&email_id, "eml");
|
||||
fs::write(file, email).map_err(Error::Io)?;
|
||||
fs::write(file, email).map_err(error::io)?;
|
||||
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
{
|
||||
if self.save_envelope {
|
||||
let file = self.path(&email_id, "json");
|
||||
let buf = serde_json::to_string(&envelope).map_err(Error::Json)?;
|
||||
fs::write(file, buf).map_err(Error::Io)?;
|
||||
let buf = serde_json::to_string(&envelope).map_err(error::envelope)?;
|
||||
fs::write(file, buf).map_err(error::io)?;
|
||||
}
|
||||
}
|
||||
// use envelope anyway
|
||||
@@ -303,14 +303,14 @@ where
|
||||
let email_id = Uuid::new_v4();
|
||||
|
||||
let file = self.inner.path(&email_id, "eml");
|
||||
E::fs_write(&file, email).await.map_err(Error::Io)?;
|
||||
E::fs_write(&file, email).await.map_err(error::io)?;
|
||||
|
||||
#[cfg(feature = "file-transport-envelope")]
|
||||
{
|
||||
if self.inner.save_envelope {
|
||||
let file = self.inner.path(&email_id, "json");
|
||||
let buf = serde_json::to_vec(&envelope).map_err(Error::Json)?;
|
||||
E::fs_write(&file, &buf).await.map_err(Error::Io)?;
|
||||
let buf = serde_json::to_vec(&envelope).map_err(error::envelope)?;
|
||||
E::fs_write(&file, &buf).await.map_err(error::io)?;
|
||||
}
|
||||
}
|
||||
// use envelope anyway
|
||||
|
||||
@@ -1,40 +1,92 @@
|
||||
//! Error and result type for sendmail transport
|
||||
|
||||
use self::Error::*;
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt::{self, Display, Formatter},
|
||||
io,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
use crate::BoxError;
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
/// An enum of all error kinds.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Internal client error
|
||||
Client(String),
|
||||
/// Error parsing UTF8 in response
|
||||
Utf8Parsing(FromUtf8Error),
|
||||
/// IO error
|
||||
Io(io::Error),
|
||||
/// The Errors that may occur when sending an email over sendmail
|
||||
pub struct Error {
|
||||
inner: Box<Inner>,
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match &self {
|
||||
Client(err) => err.fmt(fmt),
|
||||
Utf8Parsing(err) => err.fmt(fmt),
|
||||
Io(err) => err.fmt(fmt),
|
||||
struct Inner {
|
||||
kind: Kind,
|
||||
source: Option<BoxError>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn new<E>(kind: Kind, source: Option<E>) -> Error
|
||||
where
|
||||
E: Into<BoxError>,
|
||||
{
|
||||
Error {
|
||||
inner: Box::new(Inner {
|
||||
kind,
|
||||
source: source.map(Into::into),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the error is from client
|
||||
pub fn is_client(&self) -> bool {
|
||||
matches!(self.inner.kind, Kind::Client)
|
||||
}
|
||||
|
||||
/// Returns true if the error comes from the response
|
||||
pub fn is_response(&self) -> bool {
|
||||
matches!(self.inner.kind, Kind::Response)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Kind {
|
||||
/// Error parsing a response
|
||||
Response,
|
||||
/// Internal client error
|
||||
Client,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut builder = f.debug_struct("lettre::transport::sendmail::Error");
|
||||
|
||||
builder.field("kind", &self.inner.kind);
|
||||
|
||||
if let Some(ref source) = self.inner.source {
|
||||
builder.field("source", source);
|
||||
}
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.inner.kind {
|
||||
Kind::Response => f.write_str("response error")?,
|
||||
Kind::Client => f.write_str("internal client error")?,
|
||||
};
|
||||
|
||||
if let Some(ref e) = self.inner.source {
|
||||
write!(f, ": {}", e)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match &self {
|
||||
Io(err) => Some(&*err),
|
||||
Utf8Parsing(err) => Some(&*err),
|
||||
_ => None,
|
||||
}
|
||||
self.inner.source.as_ref().map(|e| {
|
||||
let r: &(dyn std::error::Error + 'static) = &**e;
|
||||
r
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn response<E: Into<BoxError>>(e: E) -> Error {
|
||||
Error::new(Kind::Response, Some(e))
|
||||
}
|
||||
|
||||
pub(crate) fn client<E: Into<BoxError>>(e: E) -> Error {
|
||||
Error::new(Kind::Client, Some(e))
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ use std::{
|
||||
|
||||
mod error;
|
||||
|
||||
const DEFAUT_SENDMAIL: &str = "/usr/sbin/sendmail";
|
||||
const DEFAULT_SENDMAIL: &str = "/usr/sbin/sendmail";
|
||||
|
||||
/// Sends emails using the `sendmail` command
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -144,7 +144,7 @@ impl SendmailTransport {
|
||||
/// Creates a new transport with the default `/usr/sbin/sendmail` command
|
||||
pub fn new() -> SendmailTransport {
|
||||
SendmailTransport {
|
||||
command: DEFAUT_SENDMAIL.into(),
|
||||
command: DEFAULT_SENDMAIL.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,21 +269,21 @@ impl Transport for SendmailTransport {
|
||||
|
||||
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
// Spawn the sendmail command
|
||||
let mut process = self.command(envelope).spawn().map_err(Error::Io)?;
|
||||
let mut process = self.command(envelope).spawn().map_err(error::client)?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(email)
|
||||
.map_err(Error::Io)?;
|
||||
let output = process.wait_with_output().map_err(Error::Io)?;
|
||||
.map_err(error::client)?;
|
||||
let output = process.wait_with_output().map_err(error::client)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr).map_err(Error::Utf8Parsing)?;
|
||||
Err(Error::Client(stderr))
|
||||
let stderr = String::from_utf8(output.stderr).map_err(error::response)?;
|
||||
Err(error::client(stderr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ impl AsyncTransport for AsyncSendmailTransport<AsyncStd1Executor> {
|
||||
let mut command = self.async_std_command(envelope);
|
||||
|
||||
// Spawn the sendmail command
|
||||
let mut process = command.spawn().map_err(Error::Io)?;
|
||||
let mut process = command.spawn().map_err(error::client)?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
@@ -308,14 +308,14 @@ impl AsyncTransport for AsyncSendmailTransport<AsyncStd1Executor> {
|
||||
.unwrap()
|
||||
.write_all(&email)
|
||||
.await
|
||||
.map_err(Error::Io)?;
|
||||
let output = process.output().await.map_err(Error::Io)?;
|
||||
.map_err(error::client)?;
|
||||
let output = process.output().await.map_err(error::client)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr).map_err(Error::Utf8Parsing)?;
|
||||
Err(Error::Client(stderr))
|
||||
let stderr = String::from_utf8(output.stderr).map_err(error::response)?;
|
||||
Err(error::client(stderr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,7 +332,7 @@ impl AsyncTransport for AsyncSendmailTransport<Tokio02Executor> {
|
||||
let mut command = self.tokio02_command(envelope);
|
||||
|
||||
// Spawn the sendmail command
|
||||
let mut process = command.spawn().map_err(Error::Io)?;
|
||||
let mut process = command.spawn().map_err(error::client)?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
@@ -340,14 +340,14 @@ impl AsyncTransport for AsyncSendmailTransport<Tokio02Executor> {
|
||||
.unwrap()
|
||||
.write_all(&email)
|
||||
.await
|
||||
.map_err(Error::Io)?;
|
||||
let output = process.wait_with_output().await.map_err(Error::Io)?;
|
||||
.map_err(error::client)?;
|
||||
let output = process.wait_with_output().await.map_err(error::client)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr).map_err(Error::Utf8Parsing)?;
|
||||
Err(Error::Client(stderr))
|
||||
let stderr = String::from_utf8(output.stderr).map_err(error::response)?;
|
||||
Err(error::client(stderr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,7 +364,7 @@ impl AsyncTransport for AsyncSendmailTransport<Tokio1Executor> {
|
||||
let mut command = self.tokio1_command(envelope);
|
||||
|
||||
// Spawn the sendmail command
|
||||
let mut process = command.spawn().map_err(Error::Io)?;
|
||||
let mut process = command.spawn().map_err(error::client)?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
@@ -372,14 +372,14 @@ impl AsyncTransport for AsyncSendmailTransport<Tokio1Executor> {
|
||||
.unwrap()
|
||||
.write_all(&email)
|
||||
.await
|
||||
.map_err(Error::Io)?;
|
||||
let output = process.wait_with_output().await.map_err(Error::Io)?;
|
||||
.map_err(error::client)?;
|
||||
let output = process.wait_with_output().await.map_err(error::client)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr).map_err(Error::Utf8Parsing)?;
|
||||
Err(Error::Client(stderr))
|
||||
let stderr = String::from_utf8(output.stderr).map_err(error::response)?;
|
||||
Err(error::client(stderr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! Error and result type for SMTP clients
|
||||
|
||||
use crate::transport::smtp::response::{Code, Severity};
|
||||
use std::{error::Error as StdError, fmt, io};
|
||||
use crate::{
|
||||
transport::smtp::response::{Code, Severity},
|
||||
BoxError,
|
||||
};
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
// Inspired by https://github.com/seanmonstar/reqwest/blob/a8566383168c0ef06c21f38cbc9213af6ff6db31/src/error.rs
|
||||
|
||||
@@ -10,8 +13,6 @@ pub struct Error {
|
||||
inner: Box<Inner>,
|
||||
}
|
||||
|
||||
pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
|
||||
|
||||
struct Inner {
|
||||
kind: Kind,
|
||||
source: Option<BoxError>,
|
||||
@@ -80,11 +81,6 @@ impl Error {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn into_io(self) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -113,7 +109,7 @@ pub(crate) enum Kind {
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut builder = f.debug_struct("lettre::Error");
|
||||
let mut builder = f.debug_struct("lettre::transport::smtp::Error");
|
||||
|
||||
builder.field("kind", &self.inner.kind);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user