use crate::models::ThingsId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use super::config::CacheDependency;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedData<T> {
pub data: T,
pub cached_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
pub dependencies: Vec<CacheDependency>,
pub access_count: u64,
pub last_accessed: DateTime<Utc>,
pub warming_priority: u32,
}
impl<T> CachedData<T> {
pub fn new(data: T, ttl: Duration) -> Self {
let now = Utc::now();
Self {
data,
cached_at: now,
expires_at: now + chrono::Duration::from_std(ttl).unwrap_or_default(),
dependencies: Vec::new(),
access_count: 0,
last_accessed: now,
warming_priority: 0,
}
}
pub fn new_with_dependencies(
data: T,
ttl: Duration,
dependencies: Vec<CacheDependency>,
) -> Self {
let now = Utc::now();
Self {
data,
cached_at: now,
expires_at: now + chrono::Duration::from_std(ttl).unwrap_or_default(),
dependencies,
access_count: 0,
last_accessed: now,
warming_priority: 0,
}
}
pub fn is_expired(&self) -> bool {
Utc::now() > self.expires_at
}
pub fn is_idle(&self, tti: Duration) -> bool {
let now = Utc::now();
let idle_duration = now - self.last_accessed;
idle_duration > chrono::Duration::from_std(tti).unwrap_or_default()
}
pub fn record_access(&mut self) {
self.access_count += 1;
self.last_accessed = Utc::now();
}
pub fn update_warming_priority(&mut self, priority: u32) {
self.warming_priority = priority;
}
pub fn add_dependency(&mut self, dependency: CacheDependency) {
self.dependencies.push(dependency);
}
pub fn has_dependency(&self, entity_type: &str, entity_id: Option<&ThingsId>) -> bool {
self.dependencies
.iter()
.any(|dep| dep.matches(entity_type, entity_id))
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub entries: u64,
pub hit_rate: f64,
pub warmed_keys: u64,
pub warming_runs: u64,
}
impl CacheStats {
pub fn calculate_hit_rate(&mut self) {
let total = self.hits + self.misses;
self.hit_rate = if total > 0 {
#[allow(clippy::cast_precision_loss)]
{
self.hits as f64 / total as f64
}
} else {
0.0
};
}
}
pub trait CachePreloader: Send + Sync + 'static {
fn predict(&self, accessed_key: &str) -> Vec<(String, u32)>;
fn warm(&self, key: &str);
}