expiring.rs

 1use std::{future::Future, time::Instant};
 2
 3use async_std::sync::Mutex;
 4
 5#[derive(Default)]
 6pub struct Expiring<T>(Mutex<Option<ExpiringState<T>>>);
 7
 8pub struct ExpiringState<T> {
 9    value: T,
10    expires_at: Instant,
11}
12
13impl<T: Clone> Expiring<T> {
14    pub async fn get_or_refresh<F, G>(&self, f: F) -> tide::Result<T>
15    where
16        F: FnOnce() -> G,
17        G: Future<Output = tide::Result<(T, Instant)>>,
18    {
19        let mut state = self.0.lock().await;
20
21        if let Some(state) = state.as_mut() {
22            if Instant::now() >= state.expires_at {
23                let (value, expires_at) = f().await?;
24                state.value = value.clone();
25                state.expires_at = expires_at;
26                Ok(value)
27            } else {
28                Ok(state.value.clone())
29            }
30        } else {
31            let (value, expires_at) = f().await?;
32            *state = Some(ExpiringState {
33                value: value.clone(),
34                expires_at,
35            });
36            Ok(value)
37        }
38    }
39
40    pub async fn clear(&self) {
41        self.0.lock().await.take();
42    }
43}