postfix_log_parser/components/
postsuper.rs

1
2//! # Postsuper 邮件队列管理组件解析器
3//!
4//! Postsuper 是 Postfix 的邮件队列管理工具组件,负责:
5//! - 管理邮件队列中的消息
6//! - 删除指定的邮件消息
7//! - 提供队列维护和清理功能
8//! - 统计队列操作的结果
9//!
10//! ## 核心功能
11//!
12//! - **邮件删除**: 根据队列 ID 删除特定邮件
13//! - **批量操作**: 批量删除多个邮件消息
14//! - **队列维护**: 清理过期和无效的队列条目
15//! - **操作统计**: 记录删除操作的数量和结果
16//!
17//! ## 支持的事件类型
18//!
19//! - **邮件删除**: 单个邮件被成功删除
20//! - **批量删除**: 批量删除操作的统计结果
21//!
22//! ## 示例日志格式
23//!
24//! ```text
25//! # 单个邮件删除
26//! 61563640322461696: removed
27//!
28//! # 批量删除统计
29//! Deleted: 1 message
30//! Deleted: 5 messages
31//! ```
32
33use crate::components::ComponentParser;
34use crate::error::ParseError;
35use crate::events::{ComponentEvent, PostsuperEvent};
36use chrono::Utc;
37use regex::Regex;
38
39/// Postsuper组件解析器
40pub struct PostsuperParser {
41    /// 邮件删除正则表达式 - 匹配 "queue_id: removed"
42    message_removed_regex: Regex,
43    /// 批量删除正则表达式 - 匹配 "Deleted: N message(s)"
44    bulk_deleted_regex: Regex,
45}
46
47impl PostsuperParser {
48    /// 创建新的Postsuper解析器
49    pub fn new() -> Result<Self, ParseError> {
50        let message_removed_regex =
51            Regex::new(r"^([A-F0-9]+):\s*removed\s*$").map_err(ParseError::RegexError)?;
52
53        let bulk_deleted_regex =
54            Regex::new(r"^Deleted:\s*(\d+)\s*messages?\s*$").map_err(ParseError::RegexError)?;
55
56        Ok(Self {
57            message_removed_regex,
58            bulk_deleted_regex,
59        })
60    }
61
62    /// 解析邮件删除事件
63    fn parse_message_removed(&self, message: &str) -> Option<PostsuperEvent> {
64        if let Some(caps) = self.message_removed_regex.captures(message) {
65            let queue_id = caps.get(1)?.as_str().to_string();
66            return Some(PostsuperEvent::message_removed(Utc::now(), queue_id));
67        }
68        None
69    }
70
71    /// 解析批量删除事件
72    fn parse_bulk_deleted(&self, message: &str) -> Option<PostsuperEvent> {
73        if let Some(caps) = self.bulk_deleted_regex.captures(message) {
74            let count_str = caps.get(1)?.as_str();
75            if let Ok(count) = count_str.parse::<u32>() {
76                return Some(PostsuperEvent::bulk_deleted(Utc::now(), count));
77            }
78        }
79        None
80    }
81}
82
83impl ComponentParser for PostsuperParser {
84    fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
85        // 尝试解析邮件删除事件
86        if let Some(event) = self.parse_message_removed(message) {
87            return Ok(ComponentEvent::Postsuper(event));
88        }
89
90        // 尝试解析批量删除事件
91        if let Some(event) = self.parse_bulk_deleted(message) {
92            return Ok(ComponentEvent::Postsuper(event));
93        }
94
95        // 如果都不匹配,返回解析错误
96        Err(ParseError::ComponentParseError {
97            component: "postsuper".to_string(),
98            reason: format!("Unknown message format: {}", message),
99        })
100    }
101
102    fn component_name(&self) -> &'static str {
103        "postsuper"
104    }
105
106    fn can_parse(&self, message: &str) -> bool {
107        self.message_removed_regex.is_match(message) || self.bulk_deleted_regex.is_match(message)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_parse_message_removed() {
117        let parser = PostsuperParser::new().unwrap();
118
119        // 测试邮件删除格式
120        let message = "61563640322461696: removed";
121        let result = parser.parse(message).unwrap();
122
123        if let ComponentEvent::Postsuper(event) = result {
124            assert_eq!(
125                event.event_type,
126                crate::events::postsuper::PostsuperEventType::MessageRemoved
127            );
128            assert_eq!(event.queue_id, Some("61563640322461696".to_string()));
129            assert_eq!(event.description, Some("removed".to_string()));
130        } else {
131            panic!("Expected PostsuperEvent::MessageRemoved");
132        }
133    }
134
135    #[test]
136    fn test_parse_bulk_deleted() {
137        let parser = PostsuperParser::new().unwrap();
138
139        // 测试批量删除格式
140        let message = "Deleted: 1 message";
141        let result = parser.parse(message).unwrap();
142
143        if let ComponentEvent::Postsuper(event) = result {
144            assert_eq!(
145                event.event_type,
146                crate::events::postsuper::PostsuperEventType::BulkDeleted
147            );
148            assert_eq!(event.message_count, Some(1));
149            assert_eq!(event.description, Some("Deleted: 1 message".to_string()));
150        } else {
151            panic!("Expected PostsuperEvent::BulkDeleted");
152        }
153    }
154
155    #[test]
156    fn test_parse_bulk_deleted_multiple() {
157        let parser = PostsuperParser::new().unwrap();
158
159        // 测试多个邮件删除格式
160        let message = "Deleted: 5 messages";
161        let result = parser.parse(message).unwrap();
162
163        if let ComponentEvent::Postsuper(event) = result {
164            assert_eq!(
165                event.event_type,
166                crate::events::postsuper::PostsuperEventType::BulkDeleted
167            );
168            assert_eq!(event.message_count, Some(5));
169            assert_eq!(event.description, Some("Deleted: 5 messages".to_string()));
170        } else {
171            panic!("Expected PostsuperEvent::BulkDeleted");
172        }
173    }
174
175    #[test]
176    fn test_can_parse() {
177        let parser = PostsuperParser::new().unwrap();
178
179        assert!(parser.can_parse("61563640322461696: removed"));
180        assert!(parser.can_parse("Deleted: 1 message"));
181        assert!(parser.can_parse("Deleted: 10 messages"));
182        assert!(!parser.can_parse("some other message"));
183    }
184}