use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};
use std::time::Instant;
use crate::state::types::NewsFeedItem;
use tracing::{debug, info, warn};
pub(super) struct CacheEntry {
pub data: Vec<NewsFeedItem>,
pub timestamp: Instant,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub(super) struct DiskCacheEntry {
pub data: Vec<NewsFeedItem>,
pub saved_at: i64,
}
pub(super) static NEWS_CACHE: LazyLock<Mutex<HashMap<String, CacheEntry>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
pub(super) const CACHE_TTL_SECONDS: u64 = 900;
pub(super) type SkipCacheEntry = Option<(Vec<NewsFeedItem>, Instant)>;
pub(super) static UPDATES_CACHE: LazyLock<Mutex<SkipCacheEntry>> =
LazyLock::new(|| Mutex::new(None));
pub(super) static AUR_COMMENTS_CACHE: LazyLock<Mutex<SkipCacheEntry>> =
LazyLock::new(|| Mutex::new(None));
pub(super) const SKIP_CACHE_TTL_SECONDS: u64 = 300;
pub(super) fn disk_cache_ttl_seconds() -> i64 {
let days = crate::theme::settings().news_cache_ttl_days.max(1);
i64::from(days) * 86400 }
pub(super) fn disk_cache_path(source: &str) -> std::path::PathBuf {
crate::theme::lists_dir().join(format!("{source}_cache.json"))
}
pub(super) fn load_from_disk_cache(source: &str) -> Option<Vec<NewsFeedItem>> {
let path = disk_cache_path(source);
let content = std::fs::read_to_string(&path).ok()?;
let entry: DiskCacheEntry = serde_json::from_str(&content).ok()?;
let now = chrono::Utc::now().timestamp();
let age = now - entry.saved_at;
let ttl = disk_cache_ttl_seconds();
if age < ttl {
info!(
source,
items = entry.data.len(),
age_hours = age / 3600,
ttl_days = ttl / 86400,
"loaded from disk cache"
);
Some(entry.data)
} else {
debug!(
source,
age_hours = age / 3600,
ttl_days = ttl / 86400,
"disk cache expired"
);
None
}
}
pub(super) fn save_to_disk_cache(source: &str, data: &[NewsFeedItem]) {
let entry = DiskCacheEntry {
data: data.to_vec(),
saved_at: chrono::Utc::now().timestamp(),
};
let path = disk_cache_path(source);
match serde_json::to_string_pretty(&entry) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
warn!(error = %e, source, "failed to write disk cache");
} else {
debug!(source, items = data.len(), "saved to disk cache");
}
}
Err(e) => warn!(error = %e, source, "failed to serialize disk cache"),
}
}