use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use super::base::BaseEvent;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event_type")]
pub enum RelayEvent {
DeliveryStatus {
#[serde(flatten)]
base: BaseEvent,
queue_id: String,
recipient: String,
relay_host: String,
relay_ip: Option<IpAddr>,
relay_port: Option<u16>,
delay: f64,
delays: DelayBreakdown,
dsn: String,
status: DeliveryStatus,
status_description: String,
},
ConnectionIssue {
#[serde(flatten)]
base: BaseEvent,
queue_id: String,
recipient: String,
relay_host: String,
relay_ip: Option<IpAddr>,
issue_type: ConnectionIssueType,
error_message: String,
},
RelayConfiguration {
#[serde(flatten)]
base: BaseEvent,
config_type: RelayConfigType,
details: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DeliveryStatus {
Sent,
Deferred,
Bounced,
Failed,
Rejected,
}
impl std::fmt::Display for DeliveryStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeliveryStatus::Sent => write!(f, "sent"),
DeliveryStatus::Deferred => write!(f, "deferred"),
DeliveryStatus::Bounced => write!(f, "bounced"),
DeliveryStatus::Failed => write!(f, "failed"),
DeliveryStatus::Rejected => write!(f, "rejected"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DelayBreakdown {
pub queue_wait: f64,
pub connection_setup: f64,
pub connection_time: f64,
pub transmission_time: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionIssueType {
LostConnection,
ConnectionTimeout,
ConnectionRefused,
DnsResolutionFailed,
TlsHandshakeFailed,
AuthenticationFailed,
Other,
}
impl std::fmt::Display for ConnectionIssueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConnectionIssueType::LostConnection => write!(f, "lost connection"),
ConnectionIssueType::ConnectionTimeout => write!(f, "connection timeout"),
ConnectionIssueType::ConnectionRefused => write!(f, "connection refused"),
ConnectionIssueType::DnsResolutionFailed => write!(f, "DNS resolution failed"),
ConnectionIssueType::TlsHandshakeFailed => write!(f, "TLS handshake failed"),
ConnectionIssueType::AuthenticationFailed => write!(f, "authentication failed"),
ConnectionIssueType::Other => write!(f, "other connection issue"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RelayConfigType {
RelayHostConfig,
TransportMapping,
AuthConfig,
TlsConfig,
}
impl RelayEvent {
pub fn queue_id(&self) -> Option<&str> {
match self {
RelayEvent::DeliveryStatus { queue_id, .. } => Some(queue_id),
RelayEvent::ConnectionIssue { queue_id, .. } => Some(queue_id),
RelayEvent::RelayConfiguration { .. } => None,
}
}
pub fn recipient(&self) -> Option<&str> {
match self {
RelayEvent::DeliveryStatus { recipient, .. } => Some(recipient),
RelayEvent::ConnectionIssue { recipient, .. } => Some(recipient),
RelayEvent::RelayConfiguration { .. } => None,
}
}
pub fn relay_host(&self) -> Option<&str> {
match self {
RelayEvent::DeliveryStatus { relay_host, .. } => Some(relay_host),
RelayEvent::ConnectionIssue { relay_host, .. } => Some(relay_host),
RelayEvent::RelayConfiguration { .. } => None,
}
}
pub fn is_successful_delivery(&self) -> bool {
matches!(
self,
RelayEvent::DeliveryStatus {
status: DeliveryStatus::Sent,
..
}
)
}
pub fn is_connection_issue(&self) -> bool {
matches!(self, RelayEvent::ConnectionIssue { .. })
}
pub fn severity(&self) -> &'static str {
match self {
RelayEvent::DeliveryStatus { status, .. } => match status {
DeliveryStatus::Sent => "info",
DeliveryStatus::Deferred => "warning",
DeliveryStatus::Bounced => "error",
DeliveryStatus::Failed => "error",
DeliveryStatus::Rejected => "error",
},
RelayEvent::ConnectionIssue { .. } => "warning",
RelayEvent::RelayConfiguration { .. } => "info",
}
}
}
impl DelayBreakdown {
pub fn from_delays_string(delays_str: &str) -> Option<Self> {
let parts: Vec<&str> = delays_str.split('/').collect();
if parts.len() != 4 {
return None;
}
Some(DelayBreakdown {
queue_wait: parts[0].parse().unwrap_or(0.0),
connection_setup: parts[1].parse().unwrap_or(0.0),
connection_time: parts[2].parse().unwrap_or(0.0),
transmission_time: parts[3].parse().unwrap_or(0.0),
})
}
pub fn total_delay(&self) -> f64 {
self.queue_wait + self.connection_setup + self.connection_time + self.transmission_time
}
}