unistore-cache 0.1.0

In-memory cache capability for UniStore
Documentation
//! 【缓存条目】- 单个缓存条目
//!
//! 职责:
//! - 包装缓存值和元数据
//! - 管理过期时间

use crate::deps::{Duration, Instant};

/// 缓存条目
#[derive(Debug, Clone)]
pub struct CacheEntry<V> {
    /// 缓存值
    value: V,
    /// 创建时间
    created_at: Instant,
    /// 最后访问时间
    last_accessed: Instant,
    /// 过期时间(None 表示永不过期)
    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)
    }

    /// 获取剩余 TTL
    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()
    }

    /// 延长 TTL
    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);
    }
}