use std::path::{Path, PathBuf};
use directories::BaseDirs;
#[derive(Debug, thiserror::Error)]
pub enum AppIdCacheError {
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
}
#[derive(Debug)]
pub struct AppIdCacheInterface {
cache_file_path: PathBuf,
}
impl AppIdCacheInterface {
pub fn new() -> Self {
let cache_file_dir = BaseDirs::new()
.map(|dirs| dirs.cache_dir().join("gamels"))
.unwrap_or_else(|| Path::new("/tmp/gamels").to_path_buf());
Self {
cache_file_path: cache_file_dir.join("appid_cache.json"),
}
}
pub fn invalidate(&mut self) -> Result<(), AppIdCacheError> {
std::fs::remove_file(&self.cache_file_path)?;
Ok(())
}
fn is_valid(&self) -> bool {
self.cache_file_path.exists()
&& self.cache_file_path.metadata().unwrap().modified().unwrap()
> std::time::SystemTime::now()
.checked_sub(std::time::Duration::from_secs(30 * 60))
.unwrap()
}
async fn refresh(&mut self) -> Result<(), AppIdCacheError> {
if let Some(cache_file_dir) = self.cache_file_path.parent() {
std::fs::create_dir_all(cache_file_dir)?;
}
let appid_cache = reqwest::get("https://api.steampowered.com/ISteamApps/GetAppList/v2/")
.await?
.json::<serde_json::Value>()
.await?
.get("applist")
.unwrap()
.get("apps")
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|app| {
(
app.get("appid").unwrap().as_u64().unwrap(),
app.get("name").unwrap().as_str().unwrap().to_string(),
)
})
.collect::<std::collections::HashMap<_, _>>();
std::fs::write(&self.cache_file_path, serde_json::to_string(&appid_cache)?)?;
Ok(())
}
pub async fn query(&mut self, appid: u64) -> Result<Option<String>, AppIdCacheError> {
if !self.is_valid() {
self.refresh().await?;
}
let appid_cache = serde_json::from_str::<std::collections::HashMap<u64, String>>(
&std::fs::read_to_string(&self.cache_file_path)?,
)?;
Ok(appid_cache.get(&appid).cloned())
}
}