postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
//! Proxymap代理映射服务模块
//!
//! 处理Postfix proxymap守护进程的事件,负责映射表的代理访问和配置管理

use serde::{Deserialize, Serialize};
use std::fmt;

/// Proxymap代理映射服务事件
///
/// 记录Postfix proxymap服务的配置警告和映射操作
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProxymapEvent {
    /// 事件发生时间戳(原始字符串格式)
    /// 保持原始时间格式,如"Apr 08 17:54:42"
    pub timestamp: String,

    /// 进程ID(字符串格式)
    /// proxymap服务进程的标识符
    pub process_id: String,

    /// 事件类型(通常为配置覆盖警告)
    pub event_type: ProxymapEventType,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ProxymapEventType {
    /// Configuration override warning
    ConfigOverrideWarning {
        file_path: String,
        line_number: u32,
        parameter: String,
        value: String,
    },
}

impl fmt::Display for ProxymapEvent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "[{}] proxymap[{}]: {}",
            self.timestamp, self.process_id, self.event_type
        )
    }
}

impl fmt::Display for ProxymapEventType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ProxymapEventType::ConfigOverrideWarning {
                file_path,
                line_number,
                parameter,
                value,
            } => {
                write!(
                    f,
                    "Config override warning: {}, line {}: overriding earlier entry: {}={}",
                    file_path, line_number, parameter, value
                )
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_override_warning_event() {
        let event = ProxymapEvent {
            timestamp: "Apr 08 17:54:42".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted".to_string(),
            },
        };

        assert_eq!(event.process_id, "80");

        let ProxymapEventType::ConfigOverrideWarning {
            file_path,
            line_number,
            parameter,
            ..
        } = &event.event_type;

        assert_eq!(file_path, "/etc/postfix/main.cf");
        assert_eq!(*line_number, 820);
        assert_eq!(parameter, "smtpd_recipient_restrictions");
    }

    #[test]
    fn test_event_serialization() {
        let event = ProxymapEvent {
            timestamp: "Apr 10 11:17:43".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 806,
                parameter: "smtpd_client_message_rate_limit".to_string(),
                value: "0".to_string(),
            },
        };

        let serialized = serde_json::to_string(&event).unwrap();
        let deserialized: ProxymapEvent = serde_json::from_str(&serialized).unwrap();
        assert_eq!(event, deserialized);
    }

    #[test]
    fn test_event_display() {
        let event = ProxymapEvent {
            timestamp: "Apr 10 11:17:43".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 826,
                parameter: "smtpd_discard_ehlo_keywords".to_string(),
                value: "silent-discard,dsn,etrn".to_string(),
            },
        };

        let display = format!("{}", event);
        assert!(display.contains("proxymap[80]"));
        assert!(display.contains("Config override warning"));
        assert!(display.contains("main.cf"));
        assert!(display.contains("line 826"));
    }

    #[test]
    fn test_complex_parameter_value() {
        let event = ProxymapEvent {
            timestamp: "Apr 08 17:54:42".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted, permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, pcre:/etc/postfix/filter_default".to_string(),
            },
        };

        let ProxymapEventType::ConfigOverrideWarning { value, .. } = &event.event_type;
        assert!(value.contains("check_client_access"));
        assert!(value.contains("permit_sasl_authenticated"));
        assert!(value.contains("reject_unauth_destination"));
    }

    #[test]
    fn test_different_parameters() {
        let events = vec![
            (
                "smtpd_recipient_restrictions",
                "check_client_access pcre:/etc/postfix/filter_trusted",
            ),
            ("smtpd_client_message_rate_limit", "0"),
            ("smtpd_discard_ehlo_keywords", "silent-discard,dsn,etrn"),
        ];

        for (param, val) in events {
            let event = ProxymapEvent {
                timestamp: "Apr 10 11:17:43".to_string(),
                process_id: "80".to_string(),
                event_type: ProxymapEventType::ConfigOverrideWarning {
                    file_path: "/etc/postfix/main.cf".to_string(),
                    line_number: 806,
                    parameter: param.to_string(),
                    value: val.to_string(),
                },
            };

            let ProxymapEventType::ConfigOverrideWarning {
                parameter, value, ..
            } = &event.event_type;

            assert_eq!(parameter, param);
            assert_eq!(value, val);
        }
    }

    #[test]
    fn test_event_equality() {
        let event1 = ProxymapEvent {
            timestamp: "Apr 08 17:54:42".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted".to_string(),
            },
        };

        let event2 = ProxymapEvent {
            timestamp: "Apr 08 17:54:42".to_string(),
            process_id: "80".to_string(),
            event_type: ProxymapEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted".to_string(),
            },
        };

        assert_eq!(event1, event2);
    }
}