Skip to main content

crepuscularity_core/
cache.rs

1//! Turbopack-style content-addressed output cache for the `.crepus` driver.
2//!
3//! [`DriverCache`] stores a SHA-256 digest of each previously written output
4//! under `.crepus-cache/`. Before the driver writes a generated file it calls
5//! [`DriverCache::is_up_to_date`]; if the new output would be byte-for-byte
6//! identical to the last write it skips the write entirely. This prevents
7//! `rustc` from seeing a bumped mtime on generated `.rs` files and triggering
8//! an unnecessary incremental rebuild.
9//!
10//! The cache is purely additive and safe to delete at any time — a cold cache
11//! just means one extra write per entry on the next run.
12
13#[cfg(not(target_arch = "wasm32"))]
14use std::path::{Path, PathBuf};
15
16#[cfg(not(target_arch = "wasm32"))]
17use sha2::{Digest, Sha256};
18
19#[cfg(not(target_arch = "wasm32"))]
20use crate::analysis::Fingerprint;
21#[cfg(not(target_arch = "wasm32"))]
22use crate::util::bytes_to_hex;
23
24/// An on-disk content-addressed cache for driver pipeline outputs.
25///
26/// Not available on `wasm32` targets (no filesystem).
27#[cfg(not(target_arch = "wasm32"))]
28pub struct DriverCache {
29    cache_dir: PathBuf,
30}
31
32#[cfg(not(target_arch = "wasm32"))]
33impl DriverCache {
34    /// Open (or create) a cache rooted at `project_root/.crepus-cache/`.
35    pub fn open(project_root: &Path) -> Self {
36        let cache_dir = project_root.join(".crepus-cache");
37        std::fs::create_dir_all(&cache_dir).ok();
38        Self { cache_dir }
39    }
40
41    /// Returns `true` if `output` is byte-for-byte identical to the last
42    /// recorded output for `fp`. Returns `false` on any I/O error or cache miss.
43    pub fn is_up_to_date(&self, fp: &Fingerprint, output: &str) -> bool {
44        let entry_path = self.entry_path(fp);
45        match std::fs::read_to_string(&entry_path) {
46            Ok(stored) => stored == self.output_hash(output),
47            Err(_) => false,
48        }
49    }
50
51    /// Record the output hash for `fp` so future calls to [`Self::is_up_to_date`]
52    /// can detect unchanged output.
53    ///
54    /// Call this **after** successfully writing the actual output file.
55    pub fn record(&self, fp: &Fingerprint, output: &str) {
56        let entry_path = self.entry_path(fp);
57        let _ = std::fs::write(&entry_path, self.output_hash(output));
58    }
59
60    fn entry_path(&self, fp: &Fingerprint) -> PathBuf {
61        self.cache_dir.join(fp.cache_key())
62    }
63
64    fn output_hash(&self, output: &str) -> String {
65        let mut h = Sha256::new();
66        h.update(output.as_bytes());
67        bytes_to_hex(h.finalize().as_ref())
68    }
69}