1use blake3::Hasher;
7use serde_json::Value;
8use std::path::{Path, PathBuf};
9
10const VERSION: &str = env!("CARGO_PKG_VERSION");
11
12#[derive(Debug, Clone)]
13pub struct FileCache {
14 dir: PathBuf,
15}
16
17impl FileCache {
18 pub fn open(dir: PathBuf) -> Option<Self> {
20 std::fs::create_dir_all(&dir).ok()?;
21 Some(Self { dir })
22 }
23
24 pub fn key(source: &[u8], path: &Path, cfg_fingerprint: &[u8]) -> String {
25 let mut h = Hasher::new();
26 h.update(b"dmc/v1");
27 h.update(VERSION.as_bytes());
28 h.update(b"\0src\0");
29 h.update(source);
30 h.update(b"\0path\0");
31 h.update(path.to_string_lossy().as_bytes());
32 h.update(b"\0cfg\0");
33 h.update(cfg_fingerprint);
34 let hex = h.finalize().to_hex();
35 hex.as_str()[..16].to_string()
36 }
37
38 pub fn get(&self, key: &str) -> Option<Value> {
39 let p = self.path_for(key);
40 let s = std::fs::read_to_string(p).ok()?;
41 serde_json::from_str(&s).ok()
42 }
43
44 pub fn put(&self, key: &str, value: &Value) {
46 let p = self.path_for(key);
47 if let Ok(json) = serde_json::to_string(value) {
48 let _ = std::fs::write(p, json);
49 }
50 }
51
52 fn path_for(&self, key: &str) -> PathBuf {
53 self.dir.join(format!("{key}.json"))
54 }
55}
56
57pub fn fingerprint<T: serde::Serialize>(cfg: &T) -> Vec<u8> {
60 let Ok(json) = serde_json::to_vec(cfg) else { return Vec::new() };
61 blake3::hash(&json).as_bytes().to_vec()
62}