Skip to main content

lumen_runtime/
cache.rs

1//! Content-addressed cache for tool invocation results.
2
3use crate::trace::hasher::canonical_hash;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fs;
7use std::path::{Path, PathBuf};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct CacheEntry {
11    pub key: String,
12    pub tool_id: String,
13    pub version: String,
14    pub policy_hash: String,
15    pub inputs_hash: String,
16    pub outputs: serde_json::Value,
17}
18
19pub struct CacheStore {
20    cache_dir: PathBuf,
21    memory: HashMap<String, CacheEntry>,
22}
23
24impl CacheStore {
25    pub fn new(base_dir: &Path) -> Self {
26        let cache_dir = base_dir.join("cache");
27        fs::create_dir_all(&cache_dir).ok();
28        Self {
29            cache_dir,
30            memory: HashMap::new(),
31        }
32    }
33
34    pub fn get(&self, key: &str) -> Option<&CacheEntry> {
35        self.memory.get(key)
36    }
37
38    pub fn put(&mut self, entry: CacheEntry) {
39        let path = self
40            .cache_dir
41            .join(format!("{}.json", &entry.key[7..71.min(entry.key.len())]));
42        if let Ok(json) = serde_json::to_string_pretty(&entry) {
43            fs::write(&path, json).ok();
44        }
45        self.memory.insert(entry.key.clone(), entry);
46    }
47
48    pub fn lookup(
49        &self,
50        tool_id: &str,
51        version: &str,
52        policy_hash: &str,
53        args: &serde_json::Value,
54    ) -> Option<&CacheEntry> {
55        let args_hash = canonical_hash(args);
56        let key = crate::trace::hasher::cache_key(tool_id, version, policy_hash, &args_hash);
57        self.get(&key)
58    }
59}