use std::{
fs::{self, File},
future::Future,
io::BufWriter,
path::PathBuf,
};
use anyhow::Result;
use directories::ProjectDirs;
use serde::{de::DeserializeOwned, Serialize};
#[derive(Debug, Clone)]
pub struct CacheManager {
path: PathBuf,
}
impl CacheManager {
pub fn new() -> Result<CacheManager> {
let path = ProjectDirs::from("", "", "dynasty-rs")
.map(|project| project.cache_dir().to_path_buf())
.unwrap_or_else(|| PathBuf::from("./dynasty-cache"));
if !path.exists() {
fs::create_dir_all(&path)?;
}
Ok(CacheManager { path })
}
fn get_cache_path(&self, identifier: &str) -> PathBuf {
self.path.join(format!(
"{}.json",
identifier
.to_lowercase()
.replace('/', "_")
.replace(' ', "_")
))
}
fn get_cache<T: DeserializeOwned + Serialize>(&self, identifier: &str) -> Option<T> {
let cache_path = self.get_cache_path(identifier);
fs::read_to_string(cache_path)
.ok()
.and_then(|content| serde_json::from_str(&content).ok())
}
fn put_cache<T: DeserializeOwned + Serialize>(
&self,
identifier: &str,
value: &T,
) -> Result<()> {
let cache_path = self.get_cache_path(identifier);
let file = File::create(cache_path)?;
let writer = BufWriter::new(file);
serde_json::to_writer(writer, value)?;
Ok(())
}
pub async fn get_or_init<T: DeserializeOwned + Serialize>(
&self,
cache_identifier: &str,
future_init: impl Future<Output = Result<T>>,
) -> Result<T> {
if let Some(cached_value) = self.get_cache::<T>(cache_identifier) {
Ok(cached_value)
} else {
let value = future_init.await?;
self.put_cache(cache_identifier, &value)?;
Ok(value)
}
}
}