tokio_cache/
timed_cache.rs1use 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 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}