use crate::event::{Event, Level};
const LEVEL_COUNT: usize = 5;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LinkCost {
Free,
Metered,
Expensive,
Offline,
}
impl LinkCost {
pub fn threshold(self) -> Level {
match self {
LinkCost::Free => Level::Trace,
LinkCost::Metered => Level::Info,
LinkCost::Expensive => Level::Warn,
LinkCost::Offline => Level::Error,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Snapshot {
pub by_level: [u32; LEVEL_COUNT],
pub emitted: u32,
pub dropped: u32,
}
pub struct Reporter {
threshold: Level,
counts: [u32; LEVEL_COUNT],
emitted: u32,
}
impl Reporter {
pub fn new(threshold: Level) -> Self {
Self {
threshold,
counts: [0; LEVEL_COUNT],
emitted: 0,
}
}
pub fn threshold(&self) -> Level {
self.threshold
}
pub fn set_threshold(&mut self, threshold: Level) {
self.threshold = threshold;
}
pub fn adapt_to(&mut self, cost: LinkCost) {
self.threshold = cost.threshold();
}
pub fn record(&mut self, event: Event) -> Option<Event> {
self.counts[event.level as usize] += 1;
if event.level >= self.threshold {
self.emitted += 1;
Some(event)
} else {
None
}
}
pub fn count(&self, level: Level) -> u32 {
self.counts[level as usize]
}
pub fn total(&self) -> u32 {
self.counts.iter().sum()
}
pub fn emitted(&self) -> u32 {
self.emitted
}
pub fn dropped(&self) -> u32 {
self.total() - self.emitted
}
pub fn snapshot(&self) -> Snapshot {
Snapshot {
by_level: self.counts,
emitted: self.emitted,
dropped: self.dropped(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_ships_at_or_above_the_threshold() {
let mut reporter = Reporter::new(Level::Info);
assert!(reporter.record(Event::debug("d")).is_none());
assert!(reporter.record(Event::info("i")).is_some());
assert!(reporter.record(Event::error("e")).is_some());
}
#[test]
fn it_counts_every_event_even_when_dropped() {
let mut reporter = Reporter::new(Level::Warn);
reporter.record(Event::debug("d"));
reporter.record(Event::debug("d"));
reporter.record(Event::error("e"));
assert_eq!(reporter.count(Level::Debug), 2);
assert_eq!(reporter.count(Level::Error), 1);
assert_eq!(reporter.total(), 3);
assert_eq!(reporter.emitted(), 1);
assert_eq!(reporter.dropped(), 2);
}
#[test]
fn link_cost_sets_the_threshold() {
let mut reporter = Reporter::new(Level::Trace);
reporter.adapt_to(LinkCost::Metered);
assert_eq!(reporter.threshold(), Level::Info);
reporter.adapt_to(LinkCost::Expensive);
assert_eq!(reporter.threshold(), Level::Warn);
reporter.adapt_to(LinkCost::Offline);
assert_eq!(reporter.threshold(), Level::Error);
reporter.adapt_to(LinkCost::Free);
assert_eq!(reporter.threshold(), Level::Trace);
}
#[test]
fn a_snapshot_summarizes_the_counters() {
let mut reporter = Reporter::new(Level::Info);
reporter.record(Event::trace("t"));
reporter.record(Event::info("i"));
reporter.record(Event::warn("w"));
let snapshot = reporter.snapshot();
assert_eq!(snapshot.by_level[Level::Trace as usize], 1);
assert_eq!(snapshot.by_level[Level::Info as usize], 1);
assert_eq!(snapshot.by_level[Level::Warn as usize], 1);
assert_eq!(snapshot.emitted, 2); assert_eq!(snapshot.dropped, 1); }
}