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