use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "event_type")]
pub enum ErrorEvent {
DeliveryDeferred {
queue_id: String,
to: String,
relay: String,
delay: f64,
delays: String,
dsn: String,
status: String,
reason: String,
error_type: ErrorType,
},
DeliveryFailed {
queue_id: String,
to: String,
relay: String,
delay: f64,
delays: String,
dsn: String,
status: String,
reason: String,
error_type: ErrorType,
},
SystemError {
queue_id: Option<String>,
error_type: ErrorType,
message: String,
},
ConnectionError {
queue_id: String,
to: String,
relay: String,
delay: f64,
delays: String,
dsn: String,
reason: String,
error_type: ErrorType,
},
Other {
queue_id: Option<String>,
error_type: String,
message: String,
},
}
impl ErrorEvent {
pub fn event_type(&self) -> &'static str {
match self {
ErrorEvent::DeliveryDeferred { .. } => "delivery_deferred",
ErrorEvent::DeliveryFailed { .. } => "delivery_failed",
ErrorEvent::SystemError { .. } => "system_error",
ErrorEvent::ConnectionError { .. } => "connection_error",
ErrorEvent::Other { .. } => "error_other",
}
}
pub fn queue_id(&self) -> Option<&str> {
match self {
ErrorEvent::DeliveryDeferred { queue_id, .. } => Some(queue_id),
ErrorEvent::DeliveryFailed { queue_id, .. } => Some(queue_id),
ErrorEvent::SystemError { queue_id, .. } => queue_id.as_deref(),
ErrorEvent::ConnectionError { queue_id, .. } => Some(queue_id),
ErrorEvent::Other { queue_id, .. } => queue_id.as_deref(),
}
}
pub fn severity(&self) -> u8 {
match self {
ErrorEvent::DeliveryDeferred { error_type, .. } => error_type.severity().level(),
ErrorEvent::DeliveryFailed { error_type, .. } => error_type.severity().level() + 1, ErrorEvent::SystemError { error_type, .. } => error_type.severity().level(),
ErrorEvent::ConnectionError { error_type, .. } => error_type.severity().level(),
ErrorEvent::Other { .. } => 2, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ErrorType {
#[serde(rename = "dns_resolution")]
DnsResolution,
#[serde(rename = "connection_refused")]
ConnectionRefused,
#[serde(rename = "connection_lost")]
ConnectionLost,
#[serde(rename = "connection_timeout")]
ConnectionTimeout,
#[serde(rename = "host_unreachable")]
HostUnreachable,
#[serde(rename = "mailbox_not_found")]
MailboxNotFound,
#[serde(rename = "mailbox_full")]
MailboxFull,
#[serde(rename = "authentication_failed")]
AuthenticationFailed,
#[serde(rename = "rate_limited")]
RateLimited,
#[serde(rename = "spam_rejected")]
SpamRejected,
#[serde(rename = "policy_rejected")]
PolicyRejected,
#[serde(rename = "system_config")]
SystemConfig,
#[serde(rename = "resource_shortage")]
ResourceShortage,
#[serde(rename = "protocol_error")]
ProtocolError,
#[serde(rename = "tls_error")]
TlsError,
#[serde(rename = "other")]
Other,
}
impl ErrorType {
pub fn from_reason(reason: &str) -> Self {
let reason_lower = reason.to_lowercase();
if reason_lower.contains("host or domain name not found")
|| reason_lower.contains("name service error")
|| reason_lower.contains("host not found")
{
Self::DnsResolution
} else if reason_lower.contains("connection refused") {
Self::ConnectionRefused
} else if reason_lower.contains("lost connection") {
Self::ConnectionLost
} else if reason_lower.contains("connection timed out") || reason_lower.contains("timeout")
{
Self::ConnectionTimeout
} else if reason_lower.contains("host unreachable")
|| reason_lower.contains("network unreachable")
{
Self::HostUnreachable
} else if reason_lower.contains("user unknown")
|| reason_lower.contains("recipient address rejected")
|| reason_lower.contains("mailbox unavailable")
{
Self::MailboxNotFound
} else if reason_lower.contains("mailbox full")
|| reason_lower.contains("quota exceeded")
|| reason_lower.contains("insufficient storage")
{
Self::MailboxFull
} else if reason_lower.contains("authentication") || reason_lower.contains("auth") {
Self::AuthenticationFailed
} else if reason_lower.contains("rate limit")
|| reason_lower.contains("too many")
|| reason_lower.contains("throttl")
{
Self::RateLimited
} else if reason_lower.contains("spam")
|| reason_lower.contains("blacklist")
|| reason_lower.contains("blocked")
{
Self::SpamRejected
} else if reason_lower.contains("policy") || reason_lower.contains("rejected") {
Self::PolicyRejected
} else if reason_lower.contains("tls")
|| reason_lower.contains("ssl")
|| reason_lower.contains("certificate")
{
Self::TlsError
} else if reason_lower.contains("protocol") {
Self::ProtocolError
} else if reason_lower.contains("config") || reason_lower.contains("permission") {
Self::SystemConfig
} else if reason_lower.contains("resource")
|| reason_lower.contains("memory")
|| reason_lower.contains("disk")
{
Self::ResourceShortage
} else {
Self::Other
}
}
pub fn description(&self) -> &'static str {
match self {
Self::DnsResolution => "DNS解析错误",
Self::ConnectionRefused => "连接被拒绝",
Self::ConnectionLost => "连接丢失",
Self::ConnectionTimeout => "连接超时",
Self::HostUnreachable => "主机不可达",
Self::MailboxNotFound => "邮箱不存在",
Self::MailboxFull => "邮箱已满",
Self::AuthenticationFailed => "认证失败",
Self::RateLimited => "速率限制",
Self::SpamRejected => "垃圾邮件拒绝",
Self::PolicyRejected => "策略拒绝",
Self::SystemConfig => "系统配置错误",
Self::ResourceShortage => "资源不足",
Self::ProtocolError => "协议错误",
Self::TlsError => "TLS/SSL错误",
Self::Other => "其他错误",
}
}
pub fn severity(&self) -> ErrorSeverity {
match self {
Self::DnsResolution | Self::ConnectionTimeout | Self::ConnectionLost => {
ErrorSeverity::Temporary
}
Self::ConnectionRefused | Self::HostUnreachable => ErrorSeverity::Network,
Self::MailboxNotFound | Self::MailboxFull => ErrorSeverity::RecipientIssue,
Self::AuthenticationFailed | Self::PolicyRejected | Self::SpamRejected => {
ErrorSeverity::PolicyIssue
}
Self::SystemConfig | Self::ResourceShortage => ErrorSeverity::SystemIssue,
Self::TlsError | Self::ProtocolError => ErrorSeverity::ProtocolIssue,
Self::RateLimited => ErrorSeverity::Temporary,
Self::Other => ErrorSeverity::Unknown,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ErrorSeverity {
#[serde(rename = "temporary")]
Temporary,
#[serde(rename = "network")]
Network,
#[serde(rename = "recipient_issue")]
RecipientIssue,
#[serde(rename = "policy_issue")]
PolicyIssue,
#[serde(rename = "system_issue")]
SystemIssue,
#[serde(rename = "protocol_issue")]
ProtocolIssue,
#[serde(rename = "unknown")]
Unknown,
}
impl ErrorSeverity {
pub fn level(&self) -> u8 {
match self {
Self::Temporary => 1,
Self::Network => 2,
Self::RecipientIssue => 3,
Self::PolicyIssue => 3,
Self::ProtocolIssue => 4,
Self::SystemIssue => 5,
Self::Unknown => 2,
}
}
pub fn description(&self) -> &'static str {
match self {
Self::Temporary => "临时性错误,通常可重试",
Self::Network => "网络连接问题",
Self::RecipientIssue => "收件人相关问题",
Self::PolicyIssue => "策略或安全问题",
Self::ProtocolIssue => "协议层面问题",
Self::SystemIssue => "系统级别问题",
Self::Unknown => "未知类型错误",
}
}
}