use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::sync::Mutex;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MetricsStore {
pub counters: HashMap<String, u64>,
#[serde(default)]
pub version: u32,
}
static METRICS: Lazy<Mutex<MetricsStore>> =
Lazy::new(|| Mutex::new(load_metrics().unwrap_or_default()));
fn metrics_path() -> PathBuf {
let base = std::env::var("XDG_DATA_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("/tmp"))
.join(".local/share")
});
base.join("assay").join("metrics.json")
}
fn load_metrics() -> Option<MetricsStore> {
let path = metrics_path();
let content = fs::read_to_string(&path).ok()?;
serde_json::from_str(&content).ok()
}
fn save_metrics(store: &MetricsStore) {
let path = metrics_path();
if let Some(parent) = path.parent() {
let _ = fs::create_dir_all(parent);
}
let temp_path = path.with_extension("json.tmp");
if let Ok(content) = serde_json::to_string_pretty(store) {
if let Ok(mut file) = fs::File::create(&temp_path) {
use std::io::Write;
if file.write_all(content.as_bytes()).is_ok() {
let _ = file.sync_all();
drop(file); let _ = fs::rename(&temp_path, &path);
}
}
}
}
pub fn increment(name: &str) {
add(name, 1);
}
pub fn add(name: &str, value: u64) {
if let Ok(mut store) = METRICS.lock() {
*store.counters.entry(name.to_string()).or_default() += value;
save_metrics(&store);
}
}
pub fn get_all() -> HashMap<String, u64> {
METRICS
.lock()
.map(|s| s.counters.clone())
.unwrap_or_default()
}
#[cfg(test)]
pub fn reset() {
if let Ok(mut store) = METRICS.lock() {
store.counters.clear();
save_metrics(&store);
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_increment() {
reset();
increment("test_counter");
increment("test_counter");
let metrics = get_all();
assert_eq!(metrics.get("test_counter"), Some(&2));
}
#[test]
#[serial]
fn test_add() {
reset();
add("test_add", 5);
add("test_add", 3);
let metrics = get_all();
assert_eq!(metrics.get("test_add"), Some(&8));
}
}