//! reuse index for immutable responses, keyed on the prior generation's manifest. //! //! the generated files ARE the cache — if the entry exists and the CAS object is //! still there, skip recomputation. commit-OID-keyed keys always produce the same bytes, //! so a manifest entry stays valid across generations as long as the CAS object lives. //! no side-file cache: the prior manifest is the single source of truth, and a missing //! CAS object just means "recompute." //! //! reuse borrows `body_hash`/`status`/`content_type` only. headers are per-route //! (same CAS bytes under `ref=main` vs `ref=` carry different cache-control), //! so the generator derives them fresh for every route spelling from the request key. use std::collections::HashMap; use crate::cas::{Cas, Digest}; use crate::manifest::GenerationManifest; use crate::request_key::RequestKey; #[derive(Debug, Default)] pub struct ReuseIndex { canonical: HashMap, } /// the content-bearing part of a prior manifest entry, by canonical key. #[derive(Debug, Clone)] pub struct ReusedResponse { pub body_hash: String, pub status: u16, pub content_type: String, } impl ReuseIndex { pub fn from_manifest(manifest: &GenerationManifest) -> Self { let mut canonical = HashMap::new(); for response in manifest.routes.values() { canonical .entry(response.cas_key.clone()) .or_insert_with(|| ReusedResponse { body_hash: response.body_hash.clone(), status: response.status, content_type: response.content_type.clone(), }); } Self { canonical } } /// body identity for this key's canonical form, if a prior generation computed it /// AND the CAS object still exists — otherwise the caller recomputes. pub fn lookup(&self, key: &RequestKey, cas: &Cas) -> Option { let reused = self.canonical.get(&key.cas_key())?; let digest = Digest::from_hex(&reused.body_hash)?; cas.has(&digest).then(|| reused.clone()) } }