use crate::deps::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct CacheEntry<V> {
value: V,
created_at: Instant,
last_accessed: Instant,
expires_at: Option<Instant>,
access_count: u64,
}
impl<V> CacheEntry<V> {
pub fn new(value: V, ttl: Option<Duration>) -> Self {
let now = Instant::now();
Self {
value,
created_at: now,
last_accessed: now,
expires_at: ttl.map(|t| now + t),
access_count: 0,
}
}
pub fn value(&self) -> &V {
&self.value
}
pub fn value_mut(&mut self) -> &mut V {
&mut self.value
}
pub fn into_value(self) -> V {
self.value
}
pub fn is_expired(&self) -> bool {
self.expires_at
.map(|exp| Instant::now() > exp)
.unwrap_or(false)
}
pub fn remaining_ttl(&self) -> Option<Duration> {
self.expires_at.and_then(|exp| {
let now = Instant::now();
if now < exp {
Some(exp - now)
} else {
None
}
})
}
pub fn touch(&mut self) {
self.last_accessed = Instant::now();
self.access_count += 1;
}
pub fn last_accessed(&self) -> Instant {
self.last_accessed
}
pub fn created_at(&self) -> Instant {
self.created_at
}
pub fn access_count(&self) -> u64 {
self.access_count
}
pub fn age(&self) -> Duration {
self.created_at.elapsed()
}
pub fn extend_ttl(&mut self, duration: Duration) {
if let Some(exp) = self.expires_at.as_mut() {
*exp = *exp + duration;
}
}
pub fn set_ttl(&mut self, ttl: Option<Duration>) {
self.expires_at = ttl.map(|t| Instant::now() + t);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_entry_creation() {
let entry = CacheEntry::new("value", Some(Duration::from_secs(10)));
assert_eq!(entry.value(), &"value");
assert!(!entry.is_expired());
}
#[test]
fn test_entry_no_ttl() {
let entry = CacheEntry::new(42, None);
assert!(!entry.is_expired());
assert!(entry.remaining_ttl().is_none());
}
#[test]
fn test_entry_touch() {
let mut entry = CacheEntry::new("value", None);
assert_eq!(entry.access_count(), 0);
entry.touch();
assert_eq!(entry.access_count(), 1);
entry.touch();
assert_eq!(entry.access_count(), 2);
}
}