Skip to main content

driven/sysinfo/
cache.rs

1//! System information caching with TTL support
2
3use super::SystemInfo;
4use std::time::{Duration, Instant};
5
6/// Cache entry with timestamp
7#[derive(Debug, Clone)]
8pub struct CacheEntry {
9    /// Cached system information
10    pub info: SystemInfo,
11    /// When the cache was populated
12    pub cached_at: Instant,
13}
14
15/// System information cache
16#[derive(Debug, Default)]
17pub struct SystemInfoCache {
18    entry: Option<CacheEntry>,
19}
20
21impl SystemInfoCache {
22    /// Create a new empty cache
23    pub fn new() -> Self {
24        Self { entry: None }
25    }
26
27    /// Get cached value if within TTL
28    pub fn get_if_valid(&self, ttl: Duration) -> Option<&SystemInfo> {
29        self.entry.as_ref().and_then(|entry| {
30            if entry.cached_at.elapsed() < ttl {
31                Some(&entry.info)
32            } else {
33                None
34            }
35        })
36    }
37
38    /// Set cached value
39    pub fn set(&mut self, info: SystemInfo) {
40        self.entry = Some(CacheEntry {
41            info,
42            cached_at: Instant::now(),
43        });
44    }
45
46    /// Invalidate the cache
47    pub fn invalidate(&mut self) {
48        self.entry = None;
49    }
50
51    /// Check if cache is valid
52    pub fn is_valid(&self, ttl: Duration) -> bool {
53        self.entry
54            .as_ref()
55            .map(|e| e.cached_at.elapsed() < ttl)
56            .unwrap_or(false)
57    }
58
59    /// Get the age of the cache
60    pub fn age(&self) -> Option<Duration> {
61        self.entry.as_ref().map(|e| e.cached_at.elapsed())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use std::thread::sleep;
69
70    fn create_test_info() -> SystemInfo {
71        SystemInfo {
72            os: super::super::OsInfo {
73                name: "test".to_string(),
74                version: "1.0".to_string(),
75                arch: "x86_64".to_string(),
76                family: "unix".to_string(),
77            },
78            shell: super::super::ShellInfo {
79                name: "bash".to_string(),
80                version: Some("5.0".to_string()),
81                path: std::path::PathBuf::from("/bin/bash"),
82            },
83            languages: vec![],
84            package_managers: vec![],
85            project: None,
86            git: None,
87            build_tools: vec![],
88            test_frameworks: vec![],
89            collected_at: std::time::SystemTime::now(),
90        }
91    }
92
93    #[test]
94    fn test_cache_empty() {
95        let cache = SystemInfoCache::new();
96        assert!(cache.get_if_valid(Duration::from_secs(60)).is_none());
97        assert!(!cache.is_valid(Duration::from_secs(60)));
98    }
99
100    #[test]
101    fn test_cache_set_get() {
102        let mut cache = SystemInfoCache::new();
103        let info = create_test_info();
104
105        cache.set(info);
106
107        assert!(cache.get_if_valid(Duration::from_secs(60)).is_some());
108        assert!(cache.is_valid(Duration::from_secs(60)));
109    }
110
111    #[test]
112    fn test_cache_invalidate() {
113        let mut cache = SystemInfoCache::new();
114        cache.set(create_test_info());
115
116        cache.invalidate();
117
118        assert!(cache.get_if_valid(Duration::from_secs(60)).is_none());
119    }
120
121    #[test]
122    fn test_cache_ttl_expiry() {
123        let mut cache = SystemInfoCache::new();
124        cache.set(create_test_info());
125
126        // Very short TTL
127        sleep(Duration::from_millis(10));
128
129        assert!(cache.get_if_valid(Duration::from_millis(1)).is_none());
130        assert!(!cache.is_valid(Duration::from_millis(1)));
131    }
132}