postfix_log_parser/events/
relay.rs1use serde::{Deserialize, Serialize};
2use std::net::IpAddr;
3
4use super::base::BaseEvent;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "event_type")]
10pub enum RelayEvent {
11 DeliveryStatus {
13 #[serde(flatten)]
14 base: BaseEvent,
15 queue_id: String,
16 recipient: String,
17 relay_host: String,
18 relay_ip: Option<IpAddr>,
19 relay_port: Option<u16>,
20 delay: f64,
21 delays: DelayBreakdown,
22 dsn: String,
23 status: DeliveryStatus,
24 status_description: String,
25 },
26 ConnectionIssue {
28 #[serde(flatten)]
29 base: BaseEvent,
30 queue_id: String,
31 recipient: String,
32 relay_host: String,
33 relay_ip: Option<IpAddr>,
34 issue_type: ConnectionIssueType,
35 error_message: String,
36 },
37 RelayConfiguration {
39 #[serde(flatten)]
40 base: BaseEvent,
41 config_type: RelayConfigType,
42 details: String,
43 },
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "lowercase")]
49pub enum DeliveryStatus {
50 Sent,
52 Deferred,
54 Bounced,
56 Failed,
58 Rejected,
60}
61
62impl std::fmt::Display for DeliveryStatus {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 DeliveryStatus::Sent => write!(f, "sent"),
66 DeliveryStatus::Deferred => write!(f, "deferred"),
67 DeliveryStatus::Bounced => write!(f, "bounced"),
68 DeliveryStatus::Failed => write!(f, "failed"),
69 DeliveryStatus::Rejected => write!(f, "rejected"),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct DelayBreakdown {
77 pub queue_wait: f64,
79 pub connection_setup: f64,
81 pub connection_time: f64,
83 pub transmission_time: f64,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum ConnectionIssueType {
91 LostConnection,
93 ConnectionTimeout,
95 ConnectionRefused,
97 DnsResolutionFailed,
99 TlsHandshakeFailed,
101 AuthenticationFailed,
103 Other,
105}
106
107impl std::fmt::Display for ConnectionIssueType {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 ConnectionIssueType::LostConnection => write!(f, "lost connection"),
111 ConnectionIssueType::ConnectionTimeout => write!(f, "connection timeout"),
112 ConnectionIssueType::ConnectionRefused => write!(f, "connection refused"),
113 ConnectionIssueType::DnsResolutionFailed => write!(f, "DNS resolution failed"),
114 ConnectionIssueType::TlsHandshakeFailed => write!(f, "TLS handshake failed"),
115 ConnectionIssueType::AuthenticationFailed => write!(f, "authentication failed"),
116 ConnectionIssueType::Other => write!(f, "other connection issue"),
117 }
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "snake_case")]
124pub enum RelayConfigType {
125 RelayHostConfig,
127 TransportMapping,
129 AuthConfig,
131 TlsConfig,
133}
134
135impl RelayEvent {
136 pub fn queue_id(&self) -> Option<&str> {
138 match self {
139 RelayEvent::DeliveryStatus { queue_id, .. } => Some(queue_id),
140 RelayEvent::ConnectionIssue { queue_id, .. } => Some(queue_id),
141 RelayEvent::RelayConfiguration { .. } => None,
142 }
143 }
144
145 pub fn recipient(&self) -> Option<&str> {
147 match self {
148 RelayEvent::DeliveryStatus { recipient, .. } => Some(recipient),
149 RelayEvent::ConnectionIssue { recipient, .. } => Some(recipient),
150 RelayEvent::RelayConfiguration { .. } => None,
151 }
152 }
153
154 pub fn relay_host(&self) -> Option<&str> {
156 match self {
157 RelayEvent::DeliveryStatus { relay_host, .. } => Some(relay_host),
158 RelayEvent::ConnectionIssue { relay_host, .. } => Some(relay_host),
159 RelayEvent::RelayConfiguration { .. } => None,
160 }
161 }
162
163 pub fn is_successful_delivery(&self) -> bool {
165 matches!(
166 self,
167 RelayEvent::DeliveryStatus {
168 status: DeliveryStatus::Sent,
169 ..
170 }
171 )
172 }
173
174 pub fn is_connection_issue(&self) -> bool {
176 matches!(self, RelayEvent::ConnectionIssue { .. })
177 }
178
179 pub fn severity(&self) -> &'static str {
181 match self {
182 RelayEvent::DeliveryStatus { status, .. } => match status {
183 DeliveryStatus::Sent => "info",
184 DeliveryStatus::Deferred => "warning",
185 DeliveryStatus::Bounced => "error",
186 DeliveryStatus::Failed => "error",
187 DeliveryStatus::Rejected => "error",
188 },
189 RelayEvent::ConnectionIssue { .. } => "warning",
190 RelayEvent::RelayConfiguration { .. } => "info",
191 }
192 }
193}
194
195impl DelayBreakdown {
196 pub fn from_delays_string(delays_str: &str) -> Option<Self> {
198 let parts: Vec<&str> = delays_str.split('/').collect();
199 if parts.len() != 4 {
200 return None;
201 }
202
203 Some(DelayBreakdown {
204 queue_wait: parts[0].parse().unwrap_or(0.0),
205 connection_setup: parts[1].parse().unwrap_or(0.0),
206 connection_time: parts[2].parse().unwrap_or(0.0),
207 transmission_time: parts[3].parse().unwrap_or(0.0),
208 })
209 }
210
211 pub fn total_delay(&self) -> f64 {
213 self.queue_wait + self.connection_setup + self.connection_time + self.transmission_time
214 }
215}