use crate::{CacheError, UnifiedCacheStats};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::hash::Hash;
#[async_trait]
pub trait UnifiedCache<K, V>: Send + Sync + Debug
where
K: Clone + Hash + Eq + Send + Sync + Debug,
V: Clone + Send + Sync + Debug,
{
async fn get(&self, key: &K) -> Option<V>;
async fn put(&self, key: K, value: V) -> Result<(), CacheError>;
async fn remove(&self, key: &K) -> bool;
async fn contains_key(&self, key: &K) -> bool;
async fn get_stats(&self) -> UnifiedCacheStats;
async fn clear(&self) -> Result<(), CacheError>;
async fn size(&self) -> usize;
async fn is_empty(&self) -> bool {
self.size().await == 0
}
async fn capacity(&self) -> usize;
fn cache_type(&self) -> &'static str;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheEntryMetadata {
pub created_at: u64,
pub last_accessed: u64,
pub access_count: u64,
pub ttl: u64,
pub size_bytes: u64,
pub cache_level: CacheLevel,
pub priority_score: f64,
}
impl CacheEntryMetadata {
pub fn new(ttl: u64, size_bytes: u64, cache_level: CacheLevel) -> Self {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
Self {
created_at: now,
last_accessed: now,
access_count: 0,
ttl,
size_bytes,
cache_level,
priority_score: 1.0,
}
}
pub fn mark_accessed(&mut self) {
self.access_count += 1;
self.last_accessed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
}
pub fn is_expired(&self) -> bool {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
now > self.created_at + self.ttl
}
pub fn retention_score(&self) -> f64 {
let age_factor =
1.0 / (1.0 + (self.last_accessed as f64 - self.created_at as f64) / 3600.0);
let frequency_factor = (self.access_count as f64).ln().max(1.0);
let size_factor = 1.0 / (1.0 + self.size_bytes as f64 / 1024.0);
self.priority_score * frequency_factor * age_factor * size_factor
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CacheLevel {
L1,
L2,
L3,
}
impl CacheLevel {
pub fn speed_rank(&self) -> u8 {
match self {
CacheLevel::L1 => 1,
CacheLevel::L2 => 2,
CacheLevel::L3 => 3,
}
}
pub fn capacity_rank(&self) -> u8 {
match self {
CacheLevel::L1 => 1,
CacheLevel::L2 => 2,
CacheLevel::L3 => 3,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheEntry<V> {
pub value: V,
pub metadata: CacheEntryMetadata,
}
impl<V> CacheEntry<V> {
pub fn new(value: V, ttl: u64, size_bytes: u64, cache_level: CacheLevel) -> Self {
Self {
value,
metadata: CacheEntryMetadata::new(ttl, size_bytes, cache_level),
}
}
pub fn access(&mut self) -> &V {
self.metadata.mark_accessed();
&self.value
}
pub fn is_expired(&self) -> bool {
self.metadata.is_expired()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_entry_metadata() {
let metadata = CacheEntryMetadata::new(3600, 1024, CacheLevel::L1);
assert_eq!(metadata.ttl, 3600);
assert_eq!(metadata.size_bytes, 1024);
assert_eq!(metadata.cache_level, CacheLevel::L1);
assert!(!metadata.is_expired());
}
#[test]
fn test_cache_level_rankings() {
assert!(CacheLevel::L1.speed_rank() < CacheLevel::L2.speed_rank());
assert!(CacheLevel::L2.speed_rank() < CacheLevel::L3.speed_rank());
assert!(CacheLevel::L1.capacity_rank() < CacheLevel::L2.capacity_rank());
}
#[test]
fn test_cache_entry() {
let entry = CacheEntry::new("test_value".to_string(), 3600, 100, CacheLevel::L1);
assert_eq!(entry.value, "test_value");
assert!(!entry.is_expired());
}
}