My personal site
cherry.computer
htmx
tailwind
axum
askama
1use std::{
2 sync::Arc,
3 time::{Duration, Instant},
4};
5use tokio::sync::RwLock;
6
7use super::Media;
8
9#[derive(Debug)]
10pub struct CachedMediaResult {
11 media_result: Option<Media>,
12 cache_time: Instant,
13 ttl: Duration,
14}
15
16impl CachedMediaResult {
17 fn has_expired(&self) -> bool {
18 let ttl = if self.media_result.is_some() {
19 self.ttl
20 } else {
21 Duration::from_secs(30)
22 };
23 self.cache_time.elapsed() >= ttl
24 }
25}
26
27pub type MediaCache = Arc<RwLock<Option<CachedMediaResult>>>;
28
29pub fn try_cache_or_fetch<F, Fut>(cache: &MediaCache, ttl: Duration, fetcher: F) -> Option<Media>
30where
31 F: FnOnce() -> Fut + Send + 'static,
32 Fut: Future<Output = anyhow::Result<Media>> + Send,
33{
34 {
35 let cached = cache.try_read().ok()?;
36 if let Some(cached) = &*cached
37 && !cached.has_expired()
38 {
39 return cached.media_result.clone();
40 }
41 }
42
43 let cache = cache.clone();
44 tokio::spawn(async move {
45 synced_fetch(&cache, ttl, fetcher).await;
46 });
47
48 None
49}
50
51pub async fn cache_or_fetch<F, Fut>(cache: &MediaCache, ttl: Duration, fetcher: F) -> Option<Media>
52where
53 F: FnOnce() -> Fut,
54 Fut: Future<Output = anyhow::Result<Media>>,
55{
56 {
57 let cached = cache.read().await;
58 if let Some(cached) = &*cached
59 && !cached.has_expired()
60 {
61 return cached.media_result.clone();
62 }
63 }
64
65 synced_fetch(cache, ttl, fetcher).await
66}
67
68async fn synced_fetch<F, Fut>(cache: &MediaCache, ttl: Duration, fetcher: F) -> Option<Media>
69where
70 F: FnOnce() -> Fut,
71 Fut: Future<Output = anyhow::Result<Media>>,
72{
73 let mut cached = cache.write().await;
74 if let Some(cached) = &*cached
75 && !cached.has_expired()
76 {
77 return cached.media_result.clone();
78 }
79
80 let result = fetcher()
81 .await
82 .map_err(|error| tracing::warn!(?error, "failed to scrape backend"))
83 .ok();
84 *cached = Some(CachedMediaResult {
85 media_result: result.clone(),
86 cache_time: Instant::now(),
87 ttl,
88 });
89 result
90}