postfix_log_parser/events/
discard.rs1use serde::{Deserialize, Serialize};
2
3use super::base::BaseEvent;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "event_type")]
10pub enum DiscardEvent {
11 MessageDiscard {
13 #[serde(flatten)]
14 base: BaseEvent,
15 queue_id: String,
16 recipient: String,
17 relay: String,
19 delay: f64,
21 delays: DelayBreakdown,
23 dsn: String,
25 status: String,
27 discard_reason: String,
29 },
30 Configuration {
32 #[serde(flatten)]
33 base: BaseEvent,
34 config_type: DiscardConfigType,
35 details: String,
36 },
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct DelayBreakdown {
42 pub queue_wait: f64,
44 pub connection_setup: f64,
46 pub connection_time: f64,
48 pub transmission_time: f64,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum DiscardConfigType {
56 TransportMapping,
58 DiscardRules,
60 ServiceStartup,
62 Other,
64}
65
66impl DiscardEvent {
67 pub fn queue_id(&self) -> Option<&str> {
69 match self {
70 DiscardEvent::MessageDiscard { queue_id, .. } => Some(queue_id),
71 DiscardEvent::Configuration { .. } => None,
72 }
73 }
74
75 pub fn recipient(&self) -> Option<&str> {
77 match self {
78 DiscardEvent::MessageDiscard { recipient, .. } => Some(recipient),
79 DiscardEvent::Configuration { .. } => None,
80 }
81 }
82
83 pub fn discard_reason(&self) -> Option<&str> {
85 match self {
86 DiscardEvent::MessageDiscard { discard_reason, .. } => Some(discard_reason),
87 DiscardEvent::Configuration { .. } => None,
88 }
89 }
90
91 pub fn is_message_discard(&self) -> bool {
93 matches!(self, DiscardEvent::MessageDiscard { .. })
94 }
95
96 pub fn severity(&self) -> &'static str {
98 match self {
99 DiscardEvent::MessageDiscard { .. } => "info", DiscardEvent::Configuration { .. } => "info", }
102 }
103
104 pub fn is_successful_discard(&self) -> bool {
106 match self {
107 DiscardEvent::MessageDiscard { status, dsn, .. } => {
108 status == "sent" && dsn.starts_with("2.") }
110 DiscardEvent::Configuration { .. } => false,
111 }
112 }
113}
114
115impl DelayBreakdown {
116 pub fn from_delays_string(delays_str: &str) -> Option<Self> {
118 let parts: Vec<&str> = delays_str.split('/').collect();
119 if parts.len() != 4 {
120 return None;
121 }
122
123 let queue_wait = parts[0].parse().ok()?;
124 let connection_setup = parts[1].parse().ok()?;
125 let connection_time = parts[2].parse().ok()?;
126 let transmission_time = parts[3].parse().ok()?;
127
128 Some(DelayBreakdown {
129 queue_wait,
130 connection_setup,
131 connection_time,
132 transmission_time,
133 })
134 }
135
136 pub fn total_delay(&self) -> f64 {
138 self.queue_wait + self.connection_setup + self.connection_time + self.transmission_time
139 }
140
141 pub fn is_fast_discard(&self) -> bool {
143 self.total_delay() < 0.1
144 }
145}
146
147impl std::fmt::Display for DiscardConfigType {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 DiscardConfigType::TransportMapping => write!(f, "transport mapping"),
151 DiscardConfigType::DiscardRules => write!(f, "discard rules"),
152 DiscardConfigType::ServiceStartup => write!(f, "service startup"),
153 DiscardConfigType::Other => write!(f, "other"),
154 }
155 }
156}