tokio_cache/
timed_cache.rs

1use std::borrow::Borrow;
2use std::future::Future;
3use std::hash::Hash;
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6
7use crate::core::{CacheCore, ExpireEntry};
8
9struct ExpiresAt;
10
11impl<V> ExpireEntry<ExpiringEntry<V>> for ExpiresAt {
12    fn is_fresh(&self, entry: &ExpiringEntry<V>) -> bool {
13        entry.is_fresh()
14    }
15}
16
17#[derive(Clone, Debug)]
18struct ExpiringEntry<T> {
19    entry: T,
20    expires_at: Instant,
21}
22
23impl<T> ExpiringEntry<T> {
24    fn fresh(entry: T, time_to_live: Duration) -> Self {
25        Self {
26            entry,
27            expires_at: Instant::now() + time_to_live,
28        }
29    }
30
31    fn into_entry(self) -> T {
32        self.entry
33    }
34
35    fn is_fresh(&self) -> bool {
36        Instant::now() < self.expires_at
37    }
38}
39
40#[derive(Clone, Debug)]
41pub struct TimedCache<K, V>
42where
43    K: Hash + Eq + Clone,
44    V: Clone,
45{
46    time_to_live: Duration,
47    core: Arc<CacheCore<K, ExpiringEntry<V>>>,
48}
49
50impl<K, V> TimedCache<K, V>
51where
52    K: Hash + Eq + Clone,
53    V: Clone,
54{
55    pub fn new(time_to_live: Duration) -> Self {
56        Self {
57            time_to_live,
58            core: Arc::new(CacheCore::new()),
59        }
60    }
61
62    /// Reads the cached entry for the given key.
63    ///
64    /// Note that this will not block.
65    pub fn read<BK>(&self, key: &BK) -> Option<V>
66    where
67        BK: Hash + Eq + ?Sized,
68        K: Borrow<BK>,
69    {
70        self.core
71            .read(key, &ExpiresAt)
72            .map(ExpiringEntry::into_entry)
73    }
74
75    pub async fn write<F, E>(&self, key: K, future: F) -> Result<V, E>
76    where
77        F: Future<Output = Result<V, E>>,
78    {
79        let time_to_live = self.time_to_live;
80        let entry = self
81            .core
82            .write(
83                key,
84                async move {
85                    let value = future.await?;
86                    Ok(ExpiringEntry::fresh(value, time_to_live))
87                },
88                &ExpiresAt,
89            )
90            .await?;
91        Ok(entry.into_entry())
92    }
93}