My personal site cherry.computer
htmx tailwind axum askama
at main 90 lines 2.1 kB view raw
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}