use serde_json::{json, Map, Value};
use crate::events::discard::{DiscardConfigType, DiscardEvent};
pub struct DiscardJsonFormatter;
impl DiscardJsonFormatter {
pub fn new() -> Self {
Self
}
pub fn format_event(&self, event: &DiscardEvent) -> Value {
let mut base_object = Map::new();
base_object.insert("component".to_string(), json!("discard"));
match event {
DiscardEvent::MessageDiscard {
base: _base,
queue_id,
recipient,
relay,
delay,
delays,
dsn,
status,
discard_reason,
} => {
base_object.insert("event_type".to_string(), json!("message_discard"));
base_object.insert("queue_id".to_string(), json!(queue_id));
base_object.insert("recipient".to_string(), json!(recipient));
base_object.insert("relay".to_string(), json!(relay));
base_object.insert("base".to_string(), json!(_base));
base_object.insert("delay_total_seconds".to_string(), json!(delay));
let delay_details = json!({
"queue_wait_seconds": delays.queue_wait,
"connection_setup_seconds": delays.connection_setup,
"connection_time_seconds": delays.connection_time,
"transmission_time_seconds": delays.transmission_time,
"total_seconds": delays.total_delay(),
"is_fast_discard": delays.is_fast_discard(),
"performance_analysis": self.analyze_discard_performance(delays)
});
base_object.insert("delay_breakdown".to_string(), delay_details);
base_object.insert("dsn_code".to_string(), json!(dsn));
base_object.insert(
"dsn_classification".to_string(),
json!(self.classify_dsn(dsn)),
);
base_object.insert("delivery_status".to_string(), json!(status));
base_object.insert("discard_reason".to_string(), json!(discard_reason));
base_object.insert(
"discard_analysis".to_string(),
self.analyze_discard_operation(dsn, status, discard_reason),
);
base_object.insert(
"mail_flow_analysis".to_string(),
self.analyze_mail_flow(recipient, discard_reason),
);
base_object.insert(
"security_assessment".to_string(),
self.assess_security_implications(recipient, discard_reason),
);
}
DiscardEvent::Configuration {
base: _base,
config_type,
details,
} => {
base_object.insert("event_type".to_string(), json!("configuration"));
base_object.insert("config_type".to_string(), json!(config_type));
base_object.insert("config_details".to_string(), json!(details));
base_object.insert("base".to_string(), json!(_base));
base_object.insert(
"config_analysis".to_string(),
self.analyze_config_event(config_type, details),
);
}
}
base_object.insert(
"discard_statistics".to_string(),
self.get_discard_statistics(event),
);
Value::Object(base_object)
}
fn analyze_discard_performance(
&self,
delays: &crate::events::discard::DelayBreakdown,
) -> Value {
let total = delays.total_delay();
let mut analysis = Map::new();
let performance_rating = if total < 0.01 {
"instant"
} else if total < 0.05 {
"very_fast"
} else if total < 0.1 {
"fast"
} else {
"slow"
};
analysis.insert("overall_rating".to_string(), json!(performance_rating));
let delay_source = if delays.queue_wait > total * 0.8 {
"queue_processing"
} else if total > 0.1 {
"system_load"
} else {
"normal_processing"
};
analysis.insert("primary_delay_source".to_string(), json!(delay_source));
analysis.insert(
"efficiency_metrics".to_string(),
json!({
"queue_wait_percentage": if total > 0.0 { (delays.queue_wait / total * 100.0).round() } else { 0.0 },
"is_optimal": delays.is_fast_discard(),
"expected_range": "0.00-0.05 seconds for optimal performance"
}),
);
Value::Object(analysis)
}
fn analyze_discard_operation(&self, dsn: &str, status: &str, discard_reason: &str) -> Value {
let mut analysis = Map::new();
let operation_validity = if status == "sent" && dsn.starts_with("2.") {
"successful_discard"
} else {
"abnormal_discard"
};
analysis.insert("operation_status".to_string(), json!(operation_validity));
let dsn_class = self.classify_dsn(dsn);
analysis.insert("dsn_classification".to_string(), json!(dsn_class));
let reason_category = if discard_reason.contains(".") {
"domain_based"
} else if discard_reason.contains("spam") || discard_reason.contains("reject") {
"security_based"
} else if discard_reason.contains("policy") {
"policy_based"
} else {
"configuration_based"
};
analysis.insert("discard_category".to_string(), json!(reason_category));
let recommended_action = match reason_category {
"security_based" => "monitor_for_patterns",
"policy_based" => "review_policy_rules",
"domain_based" => "verify_domain_configuration",
_ => "none_required",
};
analysis.insert("recommended_action".to_string(), json!(recommended_action));
Value::Object(analysis)
}
fn analyze_mail_flow(&self, recipient: &str, discard_reason: &str) -> Value {
let mut analysis = Map::new();
let recipient_domain = recipient.split('@').nth(1).unwrap_or("unknown");
analysis.insert("recipient_domain".to_string(), json!(recipient_domain));
let traffic_type = if discard_reason == recipient_domain {
"domain_redirect"
} else if discard_reason.contains("blackhole") {
"spam_filtering"
} else if discard_reason.contains("test") {
"testing_traffic"
} else {
"policy_discard"
};
analysis.insert("traffic_type".to_string(), json!(traffic_type));
let impact_level = match traffic_type {
"spam_filtering" => "positive",
"testing_traffic" => "neutral",
"policy_discard" => "informational",
_ => "informational",
};
analysis.insert("impact_level".to_string(), json!(impact_level));
Value::Object(analysis)
}
fn assess_security_implications(&self, _recipient: &str, discard_reason: &str) -> Value {
let mut assessment = Map::new();
let security_level = if discard_reason.contains("spam")
|| discard_reason.contains("virus")
|| discard_reason.contains("malware")
{
"high_security_event"
} else if discard_reason.contains("policy") || discard_reason.contains("filter") {
"policy_enforcement"
} else {
"normal_operation"
};
assessment.insert("security_level".to_string(), json!(security_level));
let threat_indicators = json!({
"potential_spam": discard_reason.contains("spam"),
"potential_malware": discard_reason.contains("virus") || discard_reason.contains("malware"),
"policy_violation": discard_reason.contains("policy"),
"suspicious_domain": discard_reason.contains("suspicious")
});
assessment.insert("threat_indicators".to_string(), threat_indicators);
let monitoring_recommendation = match security_level {
"high_security_event" => "increase_monitoring",
"policy_enforcement" => "log_for_compliance",
_ => "standard_logging",
};
assessment.insert(
"monitoring_recommendation".to_string(),
json!(monitoring_recommendation),
);
Value::Object(assessment)
}
fn analyze_config_event(&self, config_type: &DiscardConfigType, details: &str) -> Value {
let mut analysis = Map::new();
let config_impact = match config_type {
DiscardConfigType::ServiceStartup => "service_lifecycle",
DiscardConfigType::TransportMapping => "routing_configuration",
DiscardConfigType::DiscardRules => "filtering_rules",
DiscardConfigType::Other => "general_configuration",
};
analysis.insert("configuration_impact".to_string(), json!(config_impact));
let operation_type = if details.contains("starting") {
"service_start"
} else if details.contains("stopping") {
"service_stop"
} else if details.contains("warning") {
"configuration_warning"
} else {
"configuration_change"
};
analysis.insert("operation_type".to_string(), json!(operation_type));
let importance_level = match config_type {
DiscardConfigType::ServiceStartup => "high",
DiscardConfigType::DiscardRules => "medium",
_ => "low",
};
analysis.insert("importance_level".to_string(), json!(importance_level));
Value::Object(analysis)
}
fn classify_dsn(&self, dsn: &str) -> &'static str {
match dsn {
"2.0.0" => "successful_discard",
"2.1.5" => "destination_valid",
"2.7.0" => "delivery_successful",
_ if dsn.starts_with("2.") => "success_code",
_ if dsn.starts_with("4.") => "temporary_failure",
_ if dsn.starts_with("5.") => "permanent_failure",
_ => "unknown_dsn",
}
}
fn get_discard_statistics(&self, event: &DiscardEvent) -> Value {
json!({
"event_category": match event {
DiscardEvent::MessageDiscard { .. } => "message_discard",
DiscardEvent::Configuration { .. } => "configuration"
},
"processing_status": "completed",
"component_health": "operational",
"typical_use_cases": [
"spam_filtering",
"policy_enforcement",
"content_filtering",
"testing_scenarios"
]
})
}
}
impl Default for DiscardJsonFormatter {
fn default() -> Self {
Self::new()
}
}