use std::{
ffi::OsString,
fs::File,
hash::{DefaultHasher, Hash, Hasher},
path::{Path, PathBuf},
};
use bincode::config;
pub use bincode::{self, Decode, Encode};
use self::bincode::encode_into_std_write;
use crate::{
text::{Text, txt},
utils::{duat_name, src_crate},
};
pub fn load<C: Decode<()> + Default + 'static>(path: impl AsRef<Path>) -> Result<C, Text> {
let mut cache_file = cache_file::<C>(path.as_ref(), false)?;
if cache_file.metadata()?.len() == 0 {
return Ok(C::default());
}
let config = config::standard();
bincode::decode_from_std_read(&mut cache_file, config).map_err(|err| txt!("{err}"))
}
pub fn store<C: Encode + 'static>(path: impl AsRef<Path>, cache: C) -> Result<usize, Text> {
let mut cache_file = cache_file::<C>(path.as_ref(), true)?;
let config = config::standard();
encode_into_std_write(cache, &mut cache_file, config).map_err(|err| txt!("{err}"))
}
pub fn delete(path: impl Into<PathBuf>) {
fn delete_cache_inner(path: PathBuf) {
let (Some(cache_dir), Some(file_name)) = (dirs_next::cache_dir(), path.file_name()) else {
return;
};
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
let hash_value = hasher.finish();
let cached_file_name = {
let mut name = OsString::from(format!("{hash_value}-"));
name.push(file_name);
name
};
let src = cache_dir
.join("duat")
.join("structs")
.join(cached_file_name);
let _ = std::fs::remove_dir_all(src);
}
delete_cache_inner(path.into());
}
pub fn delete_for<C: 'static>(path: impl AsRef<Path>) {
let path = path.as_ref();
let (Some(cache_dir), Some(file_name)) = (dirs_next::cache_dir(), path.file_name()) else {
return;
};
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
let hash_value = hasher.finish();
let cached_file_name = {
let mut name = OsString::from(format!("{hash_value}-"));
name.push(file_name);
name
};
let src = cache_dir
.join("duat")
.join("structs")
.join(cached_file_name)
.join(format!("{}-{}", src_crate::<C>(), duat_name::<C>()));
if let Ok(true) = src.try_exists() {
std::fs::remove_file(src).unwrap();
}
}
fn cache_file<C: 'static>(path: &Path, truncate: bool) -> std::io::Result<File> {
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
let hash_value = hasher.finish();
let cached_file_name = {
let mut name = OsString::from(format!("{hash_value}-"));
name.push(path.file_name().ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::IsADirectory,
format!("{path:?} is a directory, not a buffer for caching"),
)
})?);
name
};
let src_dir = dirs_next::cache_dir()
.ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "cache directory isn't set")
})?
.join("duat")
.join("structs")
.join(cached_file_name.clone());
if !src_dir.exists() {
std::fs::create_dir_all(src_dir.clone())?;
}
let src = src_dir.join(format!("{}-{}", src_crate::<C>(), duat_name::<C>()));
std::fs::OpenOptions::new()
.create(true)
.read(true)
.write(true)
.truncate(truncate)
.open(src)
}