Skip to main content

fail2ban_log_parser_core/
lib.rs

1#![warn(clippy::pedantic)]
2
3pub use crate::parser::{Fail2BanEvent, Fail2BanHeaderType, Fail2BanLevel, Fail2BanStructuredLog};
4use std::fmt;
5
6mod parser;
7
8/// Error returned when a log line fails to parse.
9#[derive(Debug, Clone)]
10pub struct ParseError {
11    pub line_number: usize,
12    pub line: String,
13}
14
15impl fmt::Display for ParseError {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        write!(
18            f,
19            "failed to parse line {}: {}",
20            self.line_number, self.line
21        )
22    }
23}
24
25impl std::error::Error for ParseError {}
26
27/// Parse fail2ban log input. Returns a lazy iterator over parse results.
28///
29/// Each line yields `Ok(Fail2BanStructuredLog)` on success or `Err(ParseError)` on failure.
30///
31/// # Examples
32///
33/// Parse a single line:
34/// ```ignore
35/// let log = parse("2024-01-01 12:00:00,123 fail2ban.filter: INFO [sshd] ...").next();
36/// ```
37///
38/// Parse an entire file, skipping errors:
39/// ```ignore
40/// let content = std::fs::read_to_string("fail2ban.log").unwrap();
41/// for log in parse(&content).flatten() {
42///     println!("{:?}", log.jail());
43/// }
44/// ```
45///
46/// Collect all errors:
47/// ```ignore
48/// let content = std::fs::read_to_string("fail2ban.log").unwrap();
49/// let (ok, err): (Vec<_>, Vec<_>) = parse(&content).partition(Result::is_ok);
50/// ```
51pub fn parse(
52    input: &str,
53) -> impl Iterator<Item = Result<Fail2BanStructuredLog<'_>, ParseError>> + '_ {
54    input.lines().enumerate().map(|(i, line)| {
55        parser::parse_log_line(&mut &*line).map_err(|_| ParseError {
56            line_number: i + 1,
57            line: line.to_string(),
58        })
59    })
60}