use std::collections::HashMap;
use std::sync::{LazyLock, Mutex, MutexGuard};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct WireCounts {
pub unknown_frame_type: u64,
pub ext_ignored: HashMap<String, u64>,
pub ext_refused: HashMap<String, u64>,
pub enum_fallback: HashMap<String, u64>,
}
static COUNTS: LazyLock<Mutex<WireCounts>> = LazyLock::new(Mutex::default);
fn counts() -> MutexGuard<'static, WireCounts> {
COUNTS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub fn wire_counters() -> WireCounts {
counts().clone()
}
pub(crate) fn count_unknown_frame_type() {
counts().unknown_frame_type += 1;
}
pub const MAX_COUNTED_NAMES: usize = 64;
pub const OTHER: &str = "other";
pub(crate) fn count_ext_ignored(name: &str) {
count_named(&mut counts().ext_ignored, name);
}
pub(crate) fn count_ext_refused(name: &str) {
count_named(&mut counts().ext_refused, name);
}
fn count_named(by_name: &mut HashMap<String, u64>, name: &str) {
let label = match by_name.contains_key(name) || by_name.len() < MAX_COUNTED_NAMES {
true => name,
false => OTHER,
};
*by_name.entry(label.to_owned()).or_default() += 1;
}
pub(crate) fn count_enum_fallback(field: &str) {
*counts().enum_fallback.entry(field.to_owned()).or_default() += 1;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_counter_keeps_64_names_apart_and_the_rest_under_other() {
let mut by_name = HashMap::new();
for n in 0..MAX_COUNTED_NAMES + 10 {
count_named(&mut by_name, &format!("example.e{n}"));
}
count_named(&mut by_name, "example.e0");
assert_eq!(by_name.len(), MAX_COUNTED_NAMES + 1);
assert_eq!(by_name[OTHER], 10);
assert_eq!(
by_name["example.e0"], 2,
"a name already kept is counted as itself"
);
}
}