use std::{ path::PathBuf, time::{Duration, SystemTime}, }; use anyhow::{Result, anyhow}; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] struct SerialCacheEntry { serial: String, expires_at_unix: u64, } fn cache_file_path() -> Result { let proj_dir = dirs::cache_dir() .ok_or_else(|| anyhow!("No cache dir"))? .join("camper-widget-refresh"); Ok(proj_dir.join("serial.json")) } pub async fn read_serial_cache() -> Result> { let path = cache_file_path()?; if !path.exists() { return Ok(None); } let content = tokio::fs::read_to_string(&path) .await .map_err(|e| anyhow!("Failed to read cache file at {}: {}", path.display(), e))?; let entry: SerialCacheEntry = serde_json::from_str(&content)?; let now = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .as_secs(); if now <= entry.expires_at_unix { Ok(Some(entry.serial)) } else { Ok(None) } } pub async fn write_serial_cache(serial: &str, ttl: Duration) -> Result<()> { let path = cache_file_path()?; if let Some(parent) = path.parent() { tokio::fs::create_dir_all(parent).await.map_err(|e| { anyhow!( "Failed to create cache directory {}: {}", parent.display(), e ) })?; } let now = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .as_secs(); let entry = SerialCacheEntry { serial: serial.to_string(), expires_at_unix: now + ttl.as_secs(), }; let data = serde_json::to_string(&entry)?; tokio::fs::write(&path, data) .await .map_err(|e| anyhow!("Failed to write cache file at {}: {}", path.display(), e))?; Ok(()) }