Skip to main content

st/mcp/
cache.rs

1//! Cache implementation for MCP server
2
3use dashmap::DashMap;
4use std::time::{Duration, Instant};
5
6/// Cached entry with expiration
7#[derive(Clone)]
8struct CacheEntry {
9    value: String,
10    expires_at: Instant,
11}
12
13/// Thread-safe cache for analysis results
14pub struct AnalysisCache {
15    entries: DashMap<String, CacheEntry>,
16    ttl: Duration,
17}
18
19impl AnalysisCache {
20    /// Create a new cache with the given TTL in seconds
21    pub fn new(ttl_seconds: u64) -> Self {
22        Self {
23            entries: DashMap::new(),
24            ttl: Duration::from_secs(ttl_seconds),
25        }
26    }
27
28    /// Get a value from the cache if it exists and hasn't expired
29    pub async fn get(&self, key: &str) -> Option<String> {
30        self.entries.get(key).and_then(|entry| {
31            if entry.expires_at > Instant::now() {
32                Some(entry.value.clone())
33            } else {
34                // Remove expired entry
35                drop(entry);
36                self.entries.remove(key);
37                None
38            }
39        })
40    }
41
42    /// Set a value in the cache
43    pub async fn set(&self, key: String, value: String) {
44        let entry = CacheEntry {
45            value,
46            expires_at: Instant::now() + self.ttl,
47        };
48        self.entries.insert(key, entry);
49    }
50
51    /// Clear all expired entries
52    pub async fn cleanup(&self) {
53        let now = Instant::now();
54        self.entries.retain(|_, entry| entry.expires_at > now);
55    }
56
57    /// Get the number of cached entries
58    pub fn len(&self) -> usize {
59        self.entries.len()
60    }
61
62    /// Check if cache is empty
63    pub fn is_empty(&self) -> bool {
64        self.entries.is_empty()
65    }
66
67    /// Clear all entries
68    pub fn clear(&self) {
69        self.entries.clear();
70    }
71
72    /// Get cache statistics
73    pub async fn stats(&self) -> CacheStats {
74        let mut total_size = 0;
75        let mut expired = 0;
76        let now = Instant::now();
77
78        for entry in self.entries.iter() {
79            total_size += entry.value.len();
80            if entry.expires_at <= now {
81                expired += 1;
82            }
83        }
84
85        CacheStats {
86            entries: self.entries.len(),
87            size: total_size,
88            expired,
89            hits: 0,   // Would need to track this
90            misses: 0, // Would need to track this
91            hit_rate: 0.0,
92        }
93    }
94}
95
96/// Cache statistics
97pub struct CacheStats {
98    pub entries: usize,
99    pub size: usize,
100    pub expired: usize,
101    pub hits: u64,
102    pub misses: u64,
103    pub hit_rate: f64,
104}