use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use std::fs;
use std::path::PathBuf;
use super::collector::MetricsSnapshot;
pub struct MetricsStorage {
path: PathBuf,
}
impl MetricsStorage {
pub fn new(data_dir: &str) -> Self {
Self {
path: PathBuf::from(data_dir).join("metrics.json"),
}
}
pub fn load(&self) -> Result<Vec<MetricsSnapshot>> {
if !self.path.exists() {
return Ok(Vec::new());
}
let content = fs::read_to_string(&self.path)
.context(format!("Failed to read metrics from {}", self.path.display()))?;
let snapshots: Vec<MetricsSnapshot> = serde_json::from_str(&content)
.context("Failed to parse metrics")?;
Ok(snapshots)
}
pub fn save(&self, snapshots: &[MetricsSnapshot]) -> Result<()> {
let content = serde_json::to_string_pretty(snapshots)
.context("Failed to serialize metrics")?;
fs::write(&self.path, content)
.context(format!("Failed to write metrics to {}", self.path.display()))?;
Ok(())
}
pub fn append(&self, snapshot: &MetricsSnapshot) -> Result<()> {
let mut snapshots = self.load()?;
snapshots.push(snapshot.clone());
if snapshots.len() > 1000 {
let drain_count = snapshots.len() - 1000;
snapshots.drain(..drain_count);
}
self.save(&snapshots)
}
pub fn get_range(&self, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<MetricsSnapshot>> {
let snapshots = self.load()?;
Ok(snapshots
.into_iter()
.filter(|s| s.timestamp >= start && s.timestamp <= end)
.collect())
}
}