postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
use lazy_static::lazy_static;
use regex::Regex;

lazy_static! {
    /// 统一的队列ID正则表达式
    /// 新格式: 10-20个字符的字母数字组合,例如: 4bG4VR5zTgz6pqsd, 4bG5rK2jJfz6pr1t
    pub static ref QUEUE_ID_REGEX: Regex = Regex::new(r"^([0-9A-Za-z]{10,20}):").unwrap();

    /// 完整的队列ID捕获正则表达式,用于替换现有组件中的模式
    pub static ref QUEUE_ID_PATTERN: &'static str = r"([0-9A-Za-z]{10,20})";
}

/// 从消息中提取队列ID
///
/// # 参数
/// * `message` - 包含队列ID的消息字符串
///
/// # 返回
/// * `Some(String)` - 如果找到队列ID
/// * `None` - 如果没有找到队列ID
pub fn extract_queue_id(message: &str) -> Option<String> {
    QUEUE_ID_REGEX
        .captures(message)
        .and_then(|captures| captures.get(1))
        .map(|m| m.as_str().to_string())
}

/// 创建包含队列ID的正则表达式模式
///
/// # 参数
/// * `pattern` - 包含`{QUEUE_ID}`占位符的正则表达式模式
///
/// # 返回
/// * 替换了占位符的完整正则表达式字符串
///
/// # 示例
/// ```
/// use postfix_log_parser::utils::queue_id::create_queue_id_pattern;
/// let pattern = create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>");
/// assert_eq!(pattern, r"^([0-9A-Za-z]{10,20}): to=<([^>]+)>");
/// ```
pub fn create_queue_id_pattern(pattern: &str) -> String {
    pattern.replace("{QUEUE_ID}", *QUEUE_ID_PATTERN)
}

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

    #[test]
    fn test_extract_queue_id_new_format() {
        let message = "4bG4VR5zTgz6pqsd: client=localhost[127.0.0.1]:60166";
        let result = extract_queue_id(message);
        assert_eq!(result, Some("4bG4VR5zTgz6pqsd".to_string()));
    }

    #[test]
    fn test_extract_queue_id_various_formats() {
        let test_cases = vec![
            (
                "4bG5rK2jJfz6pr1t: message-id=<test@example.com>",
                Some("4bG5rK2jJfz6pr1t".to_string()),
            ),
            (
                "4bG6s970Nhz6qsfY: to=<user@example.com>",
                Some("4bG6s970Nhz6qsfY".to_string()),
            ),
            (
                "4bG6sC3LJbz6qsfc: from=<sender@example.com>",
                Some("4bG6sC3LJbz6qsfc".to_string()),
            ),
            ("no queue id in this message", None),
            ("ABC123: too short", None),
            ("4bG5rK2jJfz6pr1tTooLong: too long", None),
        ];

        for (input, expected) in test_cases {
            assert_eq!(
                extract_queue_id(input),
                expected,
                "Failed for input: {}",
                input
            );
        }
    }

    #[test]
    fn test_create_queue_id_pattern() {
        let pattern = create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>");
        assert_eq!(pattern, r"^([0-9A-Za-z]{10,20}): to=<([^>]+)>");
    }
}