use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::facts::FileFacts;
use crate::{SourceUnit, StateSymbol};
const FORMAT_VERSION: u32 = 1;
#[derive(Serialize, Deserialize)]
struct Entry {
key: String,
facts: FileFacts,
}
pub(crate) struct Cache {
dir: PathBuf,
}
impl Cache {
pub(crate) fn new(dir: &Path) -> Cache {
Cache {
dir: dir.join("derive"),
}
}
pub(crate) fn load(&self, key: &str) -> Option<FileFacts> {
let text = std::fs::read_to_string(self.path(key)).ok()?;
let entry: Entry = serde_json::from_str(&text).ok()?;
(entry.key == key).then_some(entry.facts)
}
pub(crate) fn store(&self, key: &str, facts: &FileFacts) {
let Ok(()) = std::fs::create_dir_all(&self.dir) else {
return;
};
if let Ok(json) = serde_json::to_string(&Entry {
key: key.to_string(),
facts: facts.clone(),
}) {
let _ = std::fs::write(self.path(key), json);
}
}
fn path(&self, key: &str) -> PathBuf {
self.dir
.join(format!("{:016x}.json", fnv1a64(key.as_bytes())))
}
}
pub(crate) fn key(
pack_id: &str,
file: &SourceUnit,
roots: &[String],
states: &[StateSymbol],
) -> String {
use std::fmt::Write;
let mut key = format!(
"v{FORMAT_VERSION}\x1f{}\x1f{}\x1f{}\x1f{}\x1f",
pack_id,
file.path.display(),
file.module,
roots.join("\x1e"),
);
for s in states {
write!(
key,
"{}\x1e{}\x1e{}\x1e{}\x1f",
s.qname,
s.identifier,
s.file.display(),
s.module
)
.expect("writing to a String cannot fail");
}
key.push('\x1f');
key.push_str(&file.text);
key
}
fn fnv1a64(bytes: &[u8]) -> u64 {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
for b in bytes {
hash ^= u64::from(*b);
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
}
hash
}