vtcode_core/tools/
cache.rs

1//! Caching system for tool results
2
3use super::types::{EnhancedCacheEntry, EnhancedCacheStats};
4use once_cell::sync::Lazy;
5use quick_cache::sync::Cache;
6use serde_json::Value;
7use std::sync::Arc;
8use std::time::Duration;
9
10/// Global file cache instance
11pub static FILE_CACHE: Lazy<FileCache> = Lazy::new(|| FileCache::new(1000));
12
13/// Enhanced file cache with quick-cache for high-performance caching
14pub struct FileCache {
15    file_cache: Arc<Cache<String, EnhancedCacheEntry<Value>>>,
16    directory_cache: Arc<Cache<String, EnhancedCacheEntry<Value>>>,
17    stats: Arc<std::sync::Mutex<EnhancedCacheStats>>,
18    max_size_bytes: usize,
19    ttl: Duration,
20}
21
22impl FileCache {
23    pub fn new(capacity: usize) -> Self {
24        Self {
25            file_cache: Arc::new(Cache::new(capacity)),
26            directory_cache: Arc::new(Cache::new(capacity / 2)),
27            stats: Arc::new(std::sync::Mutex::new(EnhancedCacheStats::default())),
28            max_size_bytes: 50 * 1024 * 1024, // 50MB default
29            ttl: Duration::from_secs(300),    // 5 minutes default
30        }
31    }
32
33    /// Get cached file content
34    pub async fn get_file(&self, key: &str) -> Option<Value> {
35        let mut stats = self.stats.lock().unwrap();
36
37        if let Some(entry) = self.file_cache.get(key) {
38            // Check if entry is still valid
39            if entry.timestamp.elapsed() < self.ttl {
40                // Note: quick-cache handles access tracking automatically
41                stats.hits += 1;
42                return Some(entry.data.clone());
43            } else {
44                // Entry expired, remove it
45                self.file_cache.remove(key);
46                stats.expired_evictions += 1;
47            }
48        }
49
50        stats.misses += 1;
51        None
52    }
53
54    /// Cache file content
55    pub async fn put_file(&self, key: String, value: Value) {
56        let size_bytes = serde_json::to_string(&value).unwrap_or_default().len();
57        let entry = EnhancedCacheEntry::new(value, size_bytes);
58
59        let mut stats = self.stats.lock().unwrap();
60
61        // Check memory limits (quick-cache handles eviction automatically, but we track stats)
62        if stats.total_size_bytes + size_bytes > self.max_size_bytes {
63            stats.memory_evictions += 1;
64        }
65
66        self.file_cache.insert(key, entry);
67        stats.entries = self.file_cache.len();
68        stats.total_size_bytes += size_bytes;
69    }
70
71    /// Get cached directory listing
72    pub async fn get_directory(&self, key: &str) -> Option<Value> {
73        let mut stats = self.stats.lock().unwrap();
74
75        if let Some(entry) = self.directory_cache.get(key) {
76            if entry.timestamp.elapsed() < self.ttl {
77                stats.hits += 1;
78                return Some(entry.data.clone());
79            } else {
80                self.directory_cache.remove(key);
81                stats.expired_evictions += 1;
82            }
83        }
84
85        stats.misses += 1;
86        None
87    }
88
89    /// Cache directory listing
90    pub async fn put_directory(&self, key: String, value: Value) {
91        let size_bytes = serde_json::to_string(&value).unwrap_or_default().len();
92        let entry = EnhancedCacheEntry::new(value, size_bytes);
93
94        let mut stats = self.stats.lock().unwrap();
95
96        self.directory_cache.insert(key, entry);
97        stats.entries += self.directory_cache.len();
98        stats.total_size_bytes += size_bytes;
99    }
100
101    /// Get cache statistics
102    pub async fn stats(&self) -> EnhancedCacheStats {
103        self.stats.lock().unwrap().clone()
104    }
105
106    /// Clear all caches
107    pub async fn clear(&self) {
108        self.file_cache.clear();
109        self.directory_cache.clear();
110        *self.stats.lock().unwrap() = EnhancedCacheStats::default();
111    }
112
113    /// Get cache capacity information
114    pub fn capacity(&self) -> (usize, usize) {
115        (
116            self.file_cache.capacity().try_into().unwrap_or(0),
117            self.directory_cache.capacity().try_into().unwrap_or(0),
118        )
119    }
120
121    /// Get current cache size
122    pub fn len(&self) -> (usize, usize) {
123        (self.file_cache.len(), self.directory_cache.len())
124    }
125}