postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
//! Sendmail兼容接口模块
//!
//! 处理Postfix sendmail兼容命令的事件,包括命令行工具的使用错误和兼容性问题

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

/// Sendmail兼容接口事件
///
/// 记录通过Postfix的sendmail兼容接口产生的事件
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SendmailEvent {
    /// 事件发生时间戳(原始字符串格式)
    /// 保持原始时间格式,如"Apr 24 17:20:55"
    pub timestamp: String,

    /// 进程ID(字符串格式)
    /// sendmail兼容命令的进程标识符
    pub process_id: String,

    /// 事件类型(通常为致命使用错误)
    pub event_type: SendmailEventType,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SendmailEventType {
    /// Fatal usage error
    FatalUsageError { message: String },
}

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

impl fmt::Display for SendmailEventType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SendmailEventType::FatalUsageError { message } => {
                write!(f, "Fatal usage error: {}", message)
            }
        }
    }
}

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

    #[test]
    fn test_fatal_usage_error_event() {
        let event = SendmailEvent {
            timestamp: "Apr 24 17:20:55".to_string(),
            process_id: "180".to_string(),
            event_type: SendmailEventType::FatalUsageError {
                message: "usage: mailq [options]".to_string(),
            },
        };

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

        let SendmailEventType::FatalUsageError { message } = &event.event_type;
        assert_eq!(message, "usage: mailq [options]");
    }

    #[test]
    fn test_event_serialization() {
        let event = SendmailEvent {
            timestamp: "Apr 24 17:20:55".to_string(),
            process_id: "180".to_string(),
            event_type: SendmailEventType::FatalUsageError {
                message: "usage: mailq [options]".to_string(),
            },
        };

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

    #[test]
    fn test_event_display() {
        let event = SendmailEvent {
            timestamp: "Apr 24 17:20:55".to_string(),
            process_id: "180".to_string(),
            event_type: SendmailEventType::FatalUsageError {
                message: "usage: mailq [options]".to_string(),
            },
        };

        let display = format!("{}", event);
        assert!(display.contains("sendmail[180]"));
        assert!(display.contains("Fatal usage error"));
        assert!(display.contains("mailq [options]"));
    }

    #[test]
    fn test_different_process_ids() {
        let process_ids = ["180", "187", "208", "216", "223", "230"];

        for pid in process_ids {
            let event = SendmailEvent {
                timestamp: "Apr 24 17:20:55".to_string(),
                process_id: pid.to_string(),
                event_type: SendmailEventType::FatalUsageError {
                    message: "usage: mailq [options]".to_string(),
                },
            };

            assert_eq!(event.process_id, pid);
        }
    }

    #[test]
    fn test_event_equality() {
        let event1 = SendmailEvent {
            timestamp: "Apr 24 17:20:55".to_string(),
            process_id: "180".to_string(),
            event_type: SendmailEventType::FatalUsageError {
                message: "usage: mailq [options]".to_string(),
            },
        };

        let event2 = SendmailEvent {
            timestamp: "Apr 24 17:20:55".to_string(),
            process_id: "180".to_string(),
            event_type: SendmailEventType::FatalUsageError {
                message: "usage: mailq [options]".to_string(),
            },
        };

        assert_eq!(event1, event2);
    }
}