mod observer;
pub use observer::LogObserver;
use std::{fmt::Display, str::FromStr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LogEvent {
hour: u8,
minute: u8,
second: u8,
pub executor: String,
pub output: String,
_private: (),
}
impl FromStr for LogEvent {
type Err = ();
fn from_str(line: &str) -> Result<Self, Self::Err> {
fn from_str_opt(line: &str) -> Option<LogEvent> {
let line = line.strip_prefix('[')?;
let (hour, line) = read_digits(line, 2)?;
let line = line.strip_prefix(':')?;
let (minute, line) = read_digits(line, 2)?;
let line = line.strip_prefix(':')?;
let (second, line) = read_digits(line, 2)?;
let line = line.strip_prefix("] [Server thread/INFO]: [")?;
let line = line.trim_end();
let line = line.strip_suffix(']')?;
let (executor, output) = line.split_once(": ")?;
Some(LogEvent {
hour,
minute,
second,
executor: executor.to_string(),
output: output.to_string(),
_private: (),
})
}
from_str_opt(line).ok_or(())
}
}
fn read_digits<N: FromStr>(string: &str, len: usize) -> Option<(N, &str)> {
if string.len() >= len && string[..len].bytes().all(|b| b.is_ascii_digit()) {
let number = string[..len].parse().ok()?;
Some((number, &string[len..]))
} else {
None
}
}
impl Display for LogEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{:02}:{:02}:{:02}] [Server thread/INFO]: [{}: {}]",
self.hour, self.minute, self.second, self.executor, self.output
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_str_to_string() {
let string = "[21:05:40] [Server thread/INFO]: [test: Added tag 'success' to test]";
let actual_event = string.parse::<LogEvent>().unwrap();
let actual_string = actual_event.to_string();
assert_eq!(actual_event.executor, "test");
assert_eq!(actual_event.output, "Added tag 'success' to test");
assert_eq!(actual_string, string);
}
}