use lru::LruCache;
use std::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex};
pub struct LruCacheWrapper<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
cache: Arc<Mutex<LruCache<K, V>>>,
}
impl<K, V> LruCacheWrapper<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
pub fn new(capacity: usize) -> Self {
let cap = NonZeroUsize::new(capacity).expect("Capacity must be non-zero");
Self {
cache: Arc::new(Mutex::new(LruCache::new(cap))),
}
}
pub fn insert(&self, key: K, value: V) {
let mut cache = self.cache.lock().unwrap();
cache.put(key, value);
}
pub fn get(&self, key: &K) -> Option<V> {
let mut cache = self.cache.lock().unwrap();
cache.get(key).cloned()
}
pub fn contains(&self, key: &K) -> bool {
let cache = self.cache.lock().unwrap();
cache.contains(key)
}
pub fn remove(&self, key: &K) -> Option<V> {
let mut cache = self.cache.lock().unwrap();
cache.pop(key)
}
pub fn clear(&self) {
let mut cache = self.cache.lock().unwrap();
cache.clear();
}
pub fn len(&self) -> usize {
let cache = self.cache.lock().unwrap();
cache.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> usize {
let cache = self.cache.lock().unwrap();
cache.cap().get()
}
}
impl<K, V> Clone for LruCacheWrapper<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
fn clone(&self) -> Self {
Self {
cache: Arc::clone(&self.cache),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lru_cache_basic() {
let cache = LruCacheWrapper::new(2);
cache.insert("key1", "value1");
cache.insert("key2", "value2");
assert_eq!(cache.get(&"key1"), Some("value1"));
assert_eq!(cache.get(&"key2"), Some("value2"));
assert_eq!(cache.len(), 2);
}
#[test]
fn test_lru_eviction() {
let cache = LruCacheWrapper::new(2);
cache.insert("key1", "value1");
cache.insert("key2", "value2");
cache.insert("key3", "value3");
assert_eq!(cache.get(&"key1"), None); assert_eq!(cache.get(&"key2"), Some("value2"));
assert_eq!(cache.get(&"key3"), Some("value3"));
}
#[test]
fn test_lru_access_updates_order() {
let cache = LruCacheWrapper::new(2);
cache.insert("key1", "value1");
cache.insert("key2", "value2");
cache.get(&"key1");
cache.insert("key3", "value3");
assert_eq!(cache.get(&"key1"), Some("value1")); assert_eq!(cache.get(&"key2"), None); assert_eq!(cache.get(&"key3"), Some("value3"));
}
#[test]
fn test_lru_contains() {
let cache = LruCacheWrapper::new(10);
cache.insert("key1", "value1");
assert!(cache.contains(&"key1"));
assert!(!cache.contains(&"key2"));
}
#[test]
fn test_lru_remove() {
let cache = LruCacheWrapper::new(10);
cache.insert("key1", "value1");
assert_eq!(cache.remove(&"key1"), Some("value1"));
assert_eq!(cache.get(&"key1"), None);
}
#[test]
fn test_lru_clear() {
let cache = LruCacheWrapper::new(10);
cache.insert("key1", "value1");
cache.insert("key2", "value2");
cache.clear();
assert_eq!(cache.len(), 0);
assert!(cache.is_empty());
}
#[test]
fn test_lru_capacity() {
let cache = LruCacheWrapper::<String, String>::new(100);
assert_eq!(cache.capacity(), 100);
}
#[test]
fn test_lru_thread_safety() {
use std::thread;
let cache = LruCacheWrapper::new(100);
let cache_clone = cache.clone();
let handle = thread::spawn(move || {
cache_clone.insert("key1", "value1");
});
cache.insert("key2", "value2");
handle.join().unwrap();
assert!(cache.contains(&"key1") || cache.contains(&"key2"));
}
}