use core::hash::{Hash, Hasher};
use std::hash::DefaultHasher;
use crate::{config::MdsfConfig, get_project_dir};
pub const CACHE_DIR: &str = "caches/";
pub struct CacheEntry {
config: String,
file_path: String,
file_content: String,
}
impl CacheEntry {
#[inline]
pub fn new(config_hash: String, file_path: &std::path::Path, file_content: &str) -> Self {
Self {
config: config_hash,
file_path: hash_text_block(&file_path.to_string_lossy()),
file_content: hash_text_block(file_content),
}
}
#[inline]
fn to_path(&self) -> std::path::PathBuf {
get_project_dir().join(CACHE_DIR).join(format!(
"{}/{}/{}/{}",
env!("CARGO_PKG_VERSION"),
self.config,
self.file_path,
self.file_content
))
}
#[inline]
pub fn get(&self) -> Option<String> {
std::fs::read_to_string(self.to_path()).ok()
}
#[inline]
pub fn set(&self, content: &str) -> Result<(), std::io::Error> {
let p = self.to_path();
if let Some(parent) = p.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(p, content)
}
#[cfg(test)]
pub fn delete(&self) -> Result<(), std::io::Error> {
let p = self.to_path();
let exists = p.try_exists()?;
if exists {
std::fs::remove_file(p)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod test_cache_entry {
use super::{CacheEntry, hash_config};
use crate::config::MdsfConfig;
#[test]
fn it_should_work() -> Result<(), std::io::Error> {
let config = MdsfConfig::default();
let file_path = std::path::Path::new("mdsf");
let original_content = "Mads was here";
let cache_entry = CacheEntry::new(hash_config(&config), file_path, original_content);
cache_entry.delete()?;
assert_eq!(None, cache_entry.get());
let updated_content = "This is the content after being set";
cache_entry.set(updated_content)?;
let cached_content = cache_entry.get();
assert_eq!(Some(updated_content.to_string()), cached_content);
Ok(())
}
}
#[inline]
pub fn hash_config(config: &MdsfConfig) -> String {
serde_json::to_string(config).map_or_else(
|_error| {
let mut hasher = DefaultHasher::new();
config.hash(&mut hasher);
format!("{}", hasher.finish())
},
|config_str| hash_text_block(&config_str),
)
}
#[cfg(test)]
mod test_hash_config {
use crate::{caching::hash_config, config::MdsfConfig};
#[test]
fn it_should_be_deterministic() {
assert_eq!(
hash_config(&MdsfConfig::default()),
hash_config(&MdsfConfig::default()),
);
}
}
#[inline]
pub fn hash_text_block(text: &str) -> String {
use sha2::Digest;
sha2::Sha256::digest(text)
.as_slice()
.iter()
.map(|value| format!("{value:x}"))
.collect::<String>()
}
#[cfg(test)]
mod test_hash_text_block {
use crate::caching::hash_text_block;
#[test]
fn it_should_be_deterministic() {
assert_eq!(
hash_text_block("mads was here"),
hash_text_block("mads was here"),
);
assert_eq!(
hash_text_block("mads was here"),
"3c90fa2a85f0d72142cea6ea8a5c1f8139e66160cb7737ab2c64e35e9555907d"
);
}
}