use std::fmt::Display;
use crate::event::{Event, Level};
use itertools::Itertools;
use regex_lite::Regex;
#[derive(Debug)]
pub struct Events {
pub events: Vec<Event>,
}
impl Events {
pub fn assert_contains(&self, matcher: &EventMatcher) {
if !self.events.iter().any(|e| matcher.matches(e)) {
panic!(
"An event with {matcher:?} was not found in the list of events:\n{}",
self.events.iter().join("\n")
)
}
}
#[allow(dead_code)]
fn assert_contains_in_order(&self, _matchers: &[EventMatcher]) {
todo!()
}
}
impl Display for Events {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
for event in &self.events {
if first {
write!(f, "{event}")?;
} else {
write!(f, "\n{event}")?;
}
first = false;
}
Ok(())
}
}
#[derive(Default, Debug)]
pub struct EventMatcher {
level: Matcher<Level>,
message: Matcher<String>,
message_regex: RegexMatcher,
target: Matcher<String>,
pub(crate) count: Count,
}
impl EventMatcher {
pub fn new() -> EventMatcher {
EventMatcher::default()
}
pub fn with_level(mut self, level: Level) -> EventMatcher {
self.level = Matcher::Matches(level);
self
}
pub fn with_target(mut self, target: &str) -> EventMatcher {
self.target = Matcher::Matches(target.to_owned());
self
}
pub fn with_message(mut self, message: &str) -> EventMatcher {
self.message = Matcher::Matches(message.to_owned());
self
}
pub fn with_message_regex(mut self, regex_pattern: &str) -> EventMatcher {
self.message_regex = RegexMatcher::new(regex_pattern);
self
}
pub fn with_count(mut self, count: Count) -> EventMatcher {
self.count = count;
self
}
pub fn matches(&self, event: &Event) -> bool {
self.level.matches(&event.level)
&& self.message.matches(&event.fields.message)
&& self.message_regex.matches(&event.fields.message)
&& self.target.matches(&event.target)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Count {
Times(usize),
Any,
GreaterThanOrEqual(usize),
LessThanOrEqual(usize),
}
impl From<usize> for Count {
fn from(item: usize) -> Self {
Self::Times(item)
}
}
impl Default for Count {
fn default() -> Self {
Count::Times(1)
}
}
#[derive(Debug, Default)]
enum Matcher<T: PartialEq> {
Matches(T),
#[default]
Any,
}
impl<T: PartialEq> Matcher<T> {
fn matches(&self, value: &T) -> bool {
match self {
Matcher::Matches(x) => value == x,
Matcher::Any => true,
}
}
}
#[derive(Debug, Default)]
enum RegexMatcher {
Matches(Regex),
#[default]
Any,
}
impl RegexMatcher {
fn new(pattern: &str) -> Self {
RegexMatcher::Matches(regex_lite::Regex::new(pattern).unwrap())
}
}
impl RegexMatcher {
fn matches(&self, value: &str) -> bool {
match self {
RegexMatcher::Matches(regex) => regex.is_match(value),
RegexMatcher::Any => true,
}
}
}