Skip to main content

oximedia_proxy/cache/
manager.rs

1//! Proxy cache manager for intelligent caching.
2
3use super::strategy::CacheStrategy;
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7/// Proxy cache manager.
8pub struct CacheManager {
9    /// Cache directory.
10    #[allow(dead_code)]
11    cache_dir: PathBuf,
12
13    /// Cache strategy.
14    strategy: CacheStrategy,
15
16    /// Cached items.
17    cache: HashMap<PathBuf, CacheEntry>,
18
19    /// Maximum cache size in bytes.
20    max_size: u64,
21
22    /// Current cache size in bytes.
23    current_size: u64,
24}
25
26impl CacheManager {
27    /// Create a new cache manager.
28    #[must_use]
29    pub fn new(cache_dir: PathBuf, max_size: u64) -> Self {
30        Self {
31            cache_dir,
32            strategy: CacheStrategy::Lru,
33            cache: HashMap::new(),
34            max_size,
35            current_size: 0,
36        }
37    }
38
39    /// Set the cache strategy.
40    pub fn set_strategy(&mut self, strategy: CacheStrategy) {
41        self.strategy = strategy;
42    }
43
44    /// Add a proxy to the cache.
45    pub fn add(&mut self, path: PathBuf, size: u64) {
46        if self.current_size + size > self.max_size {
47            self.evict(size);
48        }
49
50        let entry = CacheEntry {
51            path: path.clone(),
52            size,
53            access_count: 0,
54            last_access: current_timestamp(),
55        };
56
57        self.cache.insert(path, entry);
58        self.current_size += size;
59    }
60
61    /// Check if a proxy is in the cache.
62    #[must_use]
63    pub fn contains(&self, path: &PathBuf) -> bool {
64        self.cache.contains_key(path)
65    }
66
67    /// Mark a proxy as accessed.
68    pub fn access(&mut self, path: &PathBuf) {
69        if let Some(entry) = self.cache.get_mut(path) {
70            entry.access_count += 1;
71            entry.last_access = current_timestamp();
72        }
73    }
74
75    /// Evict items to make room for new size.
76    fn evict(&mut self, needed_size: u64) {
77        let mut freed = 0u64;
78        let mut to_remove = Vec::new();
79
80        // Sort entries by strategy
81        let mut entries: Vec<_> = self.cache.values().collect();
82        entries.sort_by(|a, b| match self.strategy {
83            CacheStrategy::Lru => a.last_access.cmp(&b.last_access),
84            CacheStrategy::Lfu => a.access_count.cmp(&b.access_count),
85            CacheStrategy::Fifo => a.last_access.cmp(&b.last_access),
86        });
87
88        for entry in entries {
89            if freed >= needed_size {
90                break;
91            }
92            to_remove.push(entry.path.clone());
93            freed += entry.size;
94        }
95
96        for path in to_remove {
97            if let Some(entry) = self.cache.remove(&path) {
98                self.current_size -= entry.size;
99            }
100        }
101    }
102
103    /// Get current cache size.
104    #[must_use]
105    pub const fn current_size(&self) -> u64 {
106        self.current_size
107    }
108
109    /// Get cache utilization percentage.
110    #[must_use]
111    pub fn utilization(&self) -> f64 {
112        if self.max_size == 0 {
113            0.0
114        } else {
115            (self.current_size as f64 / self.max_size as f64) * 100.0
116        }
117    }
118}
119
120/// Cache entry.
121#[derive(Debug, Clone)]
122struct CacheEntry {
123    path: PathBuf,
124    size: u64,
125    access_count: u64,
126    last_access: i64,
127}
128
129fn current_timestamp() -> i64 {
130    std::time::SystemTime::now()
131        .duration_since(std::time::UNIX_EPOCH)
132        .expect("infallible: system clock is always after UNIX_EPOCH")
133        .as_secs() as i64
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_cache_manager() {
142        let temp_dir = std::env::temp_dir();
143        let mut manager = CacheManager::new(temp_dir, 1000);
144
145        manager.add(PathBuf::from("proxy1.mp4"), 100);
146        assert_eq!(manager.current_size(), 100);
147
148        manager.add(PathBuf::from("proxy2.mp4"), 200);
149        assert_eq!(manager.current_size(), 300);
150    }
151
152    #[test]
153    fn test_cache_eviction() {
154        let temp_dir = std::env::temp_dir();
155        let mut manager = CacheManager::new(temp_dir, 500);
156
157        manager.add(PathBuf::from("proxy1.mp4"), 200);
158        manager.add(PathBuf::from("proxy2.mp4"), 200);
159        manager.add(PathBuf::from("proxy3.mp4"), 200);
160
161        // Should have evicted oldest entries
162        assert!(manager.current_size() <= 500);
163    }
164}