use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
use crate::types::SearchType;
static OVERRIDE_COUNTS: OnceLock<Mutex<HashMap<(SearchType, SearchType), u64>>> = OnceLock::new();
fn counts() -> &'static Mutex<HashMap<(SearchType, SearchType), u64>> {
OVERRIDE_COUNTS.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn record_override(routed: SearchType, override_type: SearchType) {
if routed == override_type {
return;
}
#[allow(clippy::unwrap_used, reason = "lock poison is unrecoverable")]
let mut guard = counts().lock().unwrap();
let key = (routed, override_type);
let entry = guard.entry(key).or_insert(0);
*entry += 1;
tracing::info!(
routed = ?routed,
user_chose = ?override_type,
total = *entry,
"Router override recorded"
);
}
pub fn override_counts_snapshot() -> HashMap<(SearchType, SearchType), u64> {
#[allow(clippy::unwrap_used, reason = "lock poison is unrecoverable")]
counts().lock().unwrap().clone()
}
pub fn clear_override_counts() {
#[allow(clippy::unwrap_used, reason = "lock poison is unrecoverable")]
counts().lock().unwrap().clear();
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code — panics are acceptable failures"
)]
mod tests {
use super::*;
#[test]
#[serial_test::serial]
fn record_override_increments() {
clear_override_counts();
record_override(SearchType::GraphCompletion, SearchType::Temporal);
record_override(SearchType::GraphCompletion, SearchType::Temporal);
let snap = override_counts_snapshot();
assert_eq!(
snap.get(&(SearchType::GraphCompletion, SearchType::Temporal))
.copied(),
Some(2)
);
}
#[test]
#[serial_test::serial]
fn same_type_not_recorded() {
clear_override_counts();
record_override(SearchType::Temporal, SearchType::Temporal);
assert!(override_counts_snapshot().is_empty());
}
}