Skip to main content

yewlish_fetch_utils/
cache.rs

1use js_sys::Date;
2use std::collections::hash_map::Iter;
3
4#[derive(Default, Clone, PartialEq)]
5pub enum CachePolicy {
6    #[default]
7    StaleWhileRevalidate,
8    CacheThenNetwork,
9    NetworkOnly,
10    CacheOnly,
11}
12
13#[derive(Default, Clone, PartialEq)]
14pub struct CacheOptions {
15    pub policy: Option<CachePolicy>,
16    pub max_age: Option<f64>,
17}
18
19#[derive(Clone, Debug, PartialEq)]
20pub struct CacheEntry {
21    pub timestamp: f64,
22    pub data: serde_json::Value,
23}
24
25pub struct Cache {
26    entries: std::collections::HashMap<String, CacheEntry>,
27    policy: CachePolicy,
28    max_age: f64,
29}
30
31impl Default for Cache {
32    fn default() -> Self {
33        Self::new(CacheOptions::default())
34    }
35}
36
37pub trait Cacheable {
38    fn policy(&self) -> &CachePolicy;
39    fn max_age(&self) -> f64;
40    fn set(&mut self, key: &str, value: &serde_json::Value, max_age: Option<f64>);
41    fn get(&self, key: &str) -> Option<&CacheEntry>;
42    fn iter(&self) -> Iter<String, CacheEntry>;
43    fn remove(&mut self, key: &str);
44    fn clear(&mut self);
45}
46
47const CACHE_MAX_AGE: f64 = 10.0 * 60.0 * 1000.0; // Ten minutes
48
49impl Cache {
50    fn new(options: CacheOptions) -> Self {
51        Self {
52            entries: std::collections::HashMap::new(),
53            policy: options.policy.unwrap_or_default(),
54            max_age: options.max_age.unwrap_or(CACHE_MAX_AGE),
55        }
56    }
57}
58
59impl Cacheable for Cache {
60    fn policy(&self) -> &CachePolicy {
61        &self.policy
62    }
63
64    fn max_age(&self) -> f64 {
65        self.max_age
66    }
67
68    fn set(&mut self, key: &str, value: &serde_json::Value, max_age: Option<f64>) {
69        self.entries.insert(
70            key.to_string(),
71            CacheEntry {
72                timestamp: Date::now() + max_age.unwrap_or(self.max_age),
73                data: value.clone(),
74            },
75        );
76    }
77
78    fn get(&self, key: &str) -> Option<&CacheEntry> {
79        if let Some(cache_entry) = self.entries.get(key) {
80            if cache_entry.timestamp > js_sys::Date::now() {
81                return Some(cache_entry);
82            }
83        }
84
85        None
86    }
87
88    #[must_use]
89    fn iter(&self) -> Iter<String, CacheEntry> {
90        self.entries.iter()
91    }
92
93    fn remove(&mut self, key: &str) {
94        self.entries.remove(key);
95    }
96
97    fn clear(&mut self) {
98        self.entries.clear();
99    }
100}