use std::collections::BTreeMap;
use std::fs;
use std::path::PathBuf;
use anyhow::Result;
use chrono::Local;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ActionUsageEntry {
pub count: u64,
pub last_used_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MetricsData {
pub quick_actions: BTreeMap<String, ActionUsageEntry>,
}
fn metrics_path() -> Option<PathBuf> {
dirs::cache_dir().map(|p| p.join("gitstack").join("metrics-v1.json"))
}
fn config_path() -> Option<PathBuf> {
dirs::config_dir().map(|p| p.join("gitstack").join("config.toml"))
}
pub fn quick_action_usage_enabled() -> bool {
let Some(path) = config_path() else {
return false;
};
let Ok(content) = fs::read_to_string(path) else {
return false;
};
let Ok(value) = content.parse::<toml::Value>() else {
return false;
};
value
.get("metrics")
.and_then(|v| v.get("quick_action_usage"))
.and_then(|v| v.as_bool())
.unwrap_or(false)
}
pub fn load_metrics() -> MetricsData {
let Some(path) = metrics_path() else {
return MetricsData::default();
};
let Ok(content) = fs::read_to_string(path) else {
return MetricsData::default();
};
serde_json::from_str(&content).unwrap_or_default()
}
pub fn save_metrics(data: &MetricsData) -> Result<()> {
let Some(path) = metrics_path() else {
return Ok(());
};
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, serde_json::to_string_pretty(data)?)?;
Ok(())
}
pub fn record_quick_action_usage(action_id: &str) -> Result<()> {
if !quick_action_usage_enabled() {
return Ok(());
}
let mut metrics = load_metrics();
let entry = metrics
.quick_actions
.entry(action_id.to_string())
.or_default();
entry.count += 1;
entry.last_used_at = Local::now().format("%Y-%m-%dT%H:%M:%S%z").to_string();
save_metrics(&metrics)
}
pub fn reset_metrics() -> Result<()> {
let Some(path) = metrics_path() else {
return Ok(());
};
if path.exists() {
fs::remove_file(path)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_metrics_defaults() {
let data = load_metrics();
assert!(data.quick_actions.len() <= 1000);
}
}