use super::ComponentParser;
use crate::error::ParseError;
use crate::events::base::BaseEvent;
use crate::events::postmap::{ErrorType, PostmapDetails, PostmapEvent, PostmapEventType};
use crate::events::ComponentEvent;
use regex::Regex;
use std::collections::HashMap;
pub struct PostmapParser {
file_error_regex: Regex,
command_error_regex: Regex,
}
impl PostmapParser {
pub fn new() -> Self {
Self {
file_error_regex: Regex::new(r"^open (.+?): (.+)$")
.expect("POSTMAP文件错误正则表达式编译失败"),
command_error_regex: Regex::new(r"^specify .+$")
.expect("POSTMAP命令错误正则表达式编译失败"),
}
}
pub fn parse_line(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
if let Some(event) = self.parse_file_error(line, base_event.clone()) {
return Some(event);
}
if let Some(event) = self.parse_command_error(line, base_event) {
return Some(event);
}
None
}
fn parse_file_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
if let Some(caps) = self.file_error_regex.captures(line) {
let file_path = caps.get(1).unwrap().as_str().to_string();
let error_message = caps.get(2).unwrap().as_str().to_string();
let error_type = if error_message.contains("No such file or directory") {
if file_path.ends_with('/') {
ErrorType::DirectoryNotFound
} else {
ErrorType::FileNotFound
}
} else {
ErrorType::FileNotFound
};
let details = PostmapDetails::new_file_error(error_type, file_path, error_message);
let event = PostmapEvent {
timestamp: base_event.timestamp,
process_id: base_event.process_id,
event_type: PostmapEventType::FileError,
details,
extensions: HashMap::new(),
};
return Some(event);
}
None
}
fn parse_command_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
if self.command_error_regex.is_match(line) {
let error_message = line.to_string();
if error_message.starts_with("open ") {
return None;
}
let command_args = if error_message.contains("specify -b -h or -m only with") {
Some("-b -h -m".to_string())
} else {
None
};
let details = PostmapDetails::new_command_error(error_message, command_args);
let event = PostmapEvent {
timestamp: base_event.timestamp,
process_id: base_event.process_id,
event_type: PostmapEventType::CommandError,
details,
extensions: HashMap::new(),
};
return Some(event);
}
None
}
}
impl ComponentParser for PostmapParser {
fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
let base_event = BaseEvent {
timestamp: chrono::Utc::now(), hostname: String::new(), component: "postmap".to_string(),
process_id: 0, log_level: crate::events::base::PostfixLogLevel::Fatal, raw_message: message.to_string(),
};
if let Some(event) = self.parse_line(message, base_event) {
Ok(ComponentEvent::Postmap(event))
} else {
Err(ParseError::ComponentParseError {
component: "postmap".to_string(),
reason: format!("无法识别的POSTMAP消息格式: {}", message),
})
}
}
fn component_name(&self) -> &'static str {
"postmap"
}
}
impl Default for PostmapParser {
fn default() -> Self {
Self::new()
}
}