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