1use dashmap::DashMap;
4use std::time::{Duration, Instant};
5
6#[derive(Clone)]
8struct CacheEntry {
9 value: String,
10 expires_at: Instant,
11}
12
13pub struct AnalysisCache {
15 entries: DashMap<String, CacheEntry>,
16 ttl: Duration,
17}
18
19impl AnalysisCache {
20 pub fn new(ttl_seconds: u64) -> Self {
22 Self {
23 entries: DashMap::new(),
24 ttl: Duration::from_secs(ttl_seconds),
25 }
26 }
27
28 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 drop(entry);
36 self.entries.remove(key);
37 None
38 }
39 })
40 }
41
42 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 pub async fn cleanup(&self) {
53 let now = Instant::now();
54 self.entries.retain(|_, entry| entry.expires_at > now);
55 }
56
57 pub fn len(&self) -> usize {
59 self.entries.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.entries.is_empty()
65 }
66
67 pub fn clear(&self) {
69 self.entries.clear();
70 }
71
72 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, misses: 0, hit_rate: 0.0,
92 }
93 }
94}
95
96pub 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}