use itertools::Itertools;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::error::ContextWrapper;
#[derive(Clone)]
pub struct Cache {
path: String,
extension: String,
}
impl Cache {
pub fn new(path: String, extension: String) -> Self {
std::fs::create_dir_all(&path).ok();
Self { path, extension }
}
pub async fn cached(&self) -> anyhow::Result<Vec<u64>> {
let mut entries = tokio::fs::read_dir(&self.path).await.anyhow()?;
let mut file_names: Vec<u64> = Vec::new();
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|s| s.to_str()) {
let asd = name.split('.').collect_vec();
file_names.push(asd[0].parse().anyhow()?);
}
}
}
file_names.sort();
Ok(file_names)
}
pub async fn remove_cache(&self, file: u64) -> anyhow::Result<()> {
let file = format!("{file}.{}", self.extension);
let path = std::path::Path::new(&self.path).join(file);
tokio::fs::remove_file(path).await.anyhow()
}
pub async fn write_cache<T: serde::Serialize>(&self, file: u64, data: T) -> anyhow::Result<()> {
let file = format!("{file}.{}", self.extension);
let path = std::path::Path::new(&self.path).join(file);
let data = postcard::to_allocvec(&data).anyhow()?;
let mut file = tokio::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.await
.anyhow()?;
file.write_all(&data).await.anyhow()?;
file.flush().await?;
Ok(())
}
pub async fn read_cache<T: for<'a> serde::Deserialize<'a>>(
&self,
file: u64,
) -> anyhow::Result<T> {
let file = format!("{file}.{}", self.extension);
let path = std::path::Path::new(&self.path).join(file);
let mut buffer = vec![];
tokio::fs::OpenOptions::new()
.read(true)
.open(path)
.await
.anyhow()?
.read_to_end(&mut buffer)
.await
.anyhow()?;
postcard::from_bytes(&buffer).anyhow()
}
}