use std::fmt;
pub type SmtpError = lettre::transport::smtp::Error;
pub type StubError = lettre::transport::stub::Error;
#[derive(Debug)]
pub enum MailConfigError {
InvalidUrl { detail: String },
EmptyHost,
TlsSetup { source: SmtpError },
}
impl MailConfigError {
pub(crate) fn invalid_url(detail: impl Into<String>) -> Self {
Self::InvalidUrl {
detail: detail.into(),
}
}
pub(crate) fn empty_host() -> Self {
Self::EmptyHost
}
pub(crate) fn tls_setup(source: SmtpError) -> Self {
Self::TlsSetup { source }
}
}
impl fmt::Display for MailConfigError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidUrl { detail } => write!(formatter, "invalid SMTP URL: {detail}"),
Self::EmptyHost => write!(formatter, "SMTP host must not be empty"),
Self::TlsSetup { source } => write!(formatter, "TLS setup failed: {source}"),
}
}
}
impl std::error::Error for MailConfigError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::TlsSetup { source } => Some(source),
Self::InvalidUrl { .. } | Self::EmptyHost => None,
}
}
}
#[derive(Debug)]
pub enum MailSendError {
Smtp { source: SmtpError },
Capture { source: StubError },
Build { source: EmailError },
}
impl MailSendError {
pub(crate) fn smtp(source: SmtpError) -> Self {
Self::Smtp { source }
}
pub(crate) fn capture(source: StubError) -> Self {
Self::Capture { source }
}
pub(crate) fn build(source: EmailError) -> Self {
Self::Build { source }
}
#[must_use]
pub fn is_smtp(&self) -> bool {
matches!(self, Self::Smtp { .. })
}
#[must_use]
pub fn is_capture(&self) -> bool {
matches!(self, Self::Capture { .. })
}
}
impl fmt::Display for MailSendError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Smtp { source } => write!(formatter, "SMTP send failed: {source}"),
Self::Capture { source } => write!(formatter, "capture send failed: {source}"),
Self::Build { source } => write!(formatter, "message build failed: {source}"),
}
}
}
impl std::error::Error for MailSendError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Smtp { source } => Some(source),
Self::Capture { source } => Some(source),
Self::Build { source } => Some(source),
}
}
}
#[derive(Debug)]
pub enum EmailError {
Build { source: lettre::error::Error },
ContentType {
source: lettre::message::header::ContentTypeErr,
},
Address {
source: lettre::address::AddressError,
},
}
impl EmailError {
pub(crate) fn build(source: lettre::error::Error) -> Self {
Self::Build { source }
}
pub(crate) fn content_type(source: lettre::message::header::ContentTypeErr) -> Self {
Self::ContentType { source }
}
pub(crate) fn address(source: lettre::address::AddressError) -> Self {
Self::Address { source }
}
}
impl fmt::Display for EmailError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Build { source } => write!(formatter, "email build failed: {source}"),
Self::ContentType { source } => write!(formatter, "invalid content type: {source}"),
Self::Address { source } => write!(formatter, "invalid email address: {source}"),
}
}
}
impl std::error::Error for EmailError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Build { source } => Some(source),
Self::ContentType { source } => Some(source),
Self::Address { source } => Some(source),
}
}
}
#[cfg(feature = "views")]
#[derive(Debug)]
#[non_exhaustive]
pub enum MailViewError {
Render { source: crate::view::ViewError },
Build { source: EmailError },
}
#[cfg(feature = "views")]
impl From<crate::view::ViewError> for MailViewError {
fn from(source: crate::view::ViewError) -> Self {
Self::Render { source }
}
}
#[cfg(feature = "views")]
impl From<EmailError> for MailViewError {
fn from(source: EmailError) -> Self {
Self::Build { source }
}
}
#[cfg(feature = "views")]
impl fmt::Display for MailViewError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Render { source } => write!(formatter, "email template failed: {source}"),
Self::Build { source } => write!(formatter, "email build failed: {source}"),
}
}
}
#[cfg(feature = "views")]
impl std::error::Error for MailViewError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Render { source } => Some(source),
Self::Build { source } => Some(source),
}
}
}
#[cfg(feature = "views")]
impl From<MailViewError> for crate::Error {
fn from(error: MailViewError) -> Self {
match error {
MailViewError::Render { source } => source.into(),
MailViewError::Build { source } => crate::Error::Mail(source.to_string()),
}
}
}