counters 0.5.0

Simple utility to count events for debugging purposes.
Documentation
use std::io;
use fxhash::FxHashMap;
use std::cell::RefCell;

use crate::filters::Filter;

/// Helper to count events for debugging purposes.
///
/// `Counters` uses internal mutability to allow updating events without
/// requiring unique ownership or mutable countexts.
#[derive(Clone, Debug, Eq)]
pub struct Counters {
    events: RefCell<FxHashMap<&'static str, u64>>
}

impl std::cmp::PartialEq for Counters {
    fn eq(&self, _other: &Self) -> bool { true }
}

impl std::hash::Hash for Counters {
    fn hash<H>(&self, _state: &mut H) where H: std::hash::Hasher {}
}

impl Counters {
    pub fn new() -> Self {
        Counters {
            events: RefCell::new(FxHashMap::default()),
        }
    }

    pub fn with_capacity(cap: usize) -> Self {
        let mut events = FxHashMap::default();
        events.reserve(cap);
        Counters { events: RefCell::new(events) }
    }

    /// Increment the counter for the provided event key.
    pub fn event(&self, key: &'static str) {
        *self.events.borrow_mut().entry(key.into()).or_insert(0) += 1
    }

    /// Set the value of the counter for a given event key.
    pub fn set(&self, key: &'static str, value: u64) {
        self.events.borrow_mut().insert(key.into(), value);
    }

    /// Reset the counter for the provided event key to zero.
    pub fn reset_event(&self, key: &'static str) {
        self.events.borrow_mut().insert(key.into(), 0);
    }

    /// Reset some of the counters to zero.
    pub fn reset_events<F: Filter>(&self, mut filter: F) {
        self.events.borrow_mut().retain(|key, val| !filter.apply(key, *val));
    }

    /// Reset all counters to zero.
    pub fn reset_all(&self) {
        self.events.borrow_mut().clear();
    }

    /// Keep some of the counters and throw away the rest.
    pub fn retain<F: Filter>(&self, mut filter: F) {
        self.events.borrow_mut().retain(|key, val| filter.apply(key, *val));
    }

    /// Get the value of the counter or zero if it does not exist.
    pub fn get(&self, key: &'static str) -> u64 {
        self.events.borrow().get(key).cloned().unwrap_or(0)
    }

    /// Return the sum of all counters with keys containing the provided filter.
    pub fn accumulate<F: Filter>(&self, mut filter: F) -> u64 {
        let mut n = 0;

        for (key, value) in self.events.borrow().iter() {
            if filter.apply(key, *value) {
                n += value
            }
        }

        n
    }

    /// Print the counters to an io stream.
    pub fn print<F: Filter>(&self, mut filter: F, to: &mut io::Write) -> io::Result<()> {
        for (key, value) in self.events.borrow().iter() {
            if filter.apply(key, *value) {
                writeln!(to, "{}: {}", key, value)?;
            }
        }

        Ok(())
    }


    /// Print the counters to stdout.
    pub fn print_to_stdout<F: Filter>(&self, filter: F) {
        let stdout = io::stdout();
        let mut to = stdout.lock();
        self.print(filter, &mut to).unwrap();
    }

    /// Moves all the elements of `other` into `self`, leaving `other` empty.
    pub fn append(&self, other: &Counters) {
        for (key, value) in other.events.borrow_mut().drain() {
            *self.events.borrow_mut().entry(key).or_insert(0) += value;
        }
    }

    /// Apply a function to all counters matching the provided filter.
    pub fn apply<F: Filter, Cb: FnMut(&'static str, u64) -> u64>(&self, mut filter: F, mut callback: Cb) {
        for (key, value) in self.events.borrow_mut().iter_mut() {
            if filter.apply(key, *value) {
                *value = callback(key, *value);
            }
        }
    }
}

#[cfg(feature = "dummy_serialization")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "dummy_serialization")]
use crate::Dummy;

#[cfg(feature = "dummy_serialization")]
impl Serialize for Counters {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::Serializer {
        Dummy.serialize(serializer)
    }
}

#[cfg(feature = "dummy_serialization")]
impl<'de> Deserialize<'de> for Counters {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where D: serde::Deserializer<'de> {
        Dummy::deserialize(deserializer)?;
        Ok(Counters::new())
    }
}