Skip to main content

sova_store/
cache.rs

1//! Thin JSON cache helper over any [`KvStore`] (Memory / File / Sql / Redis).
2
3use crate::{namespace, AppStore, KvStore};
4use bytes::Bytes;
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use std::future::Future;
8use std::sync::Arc;
9use std::time::Duration;
10use thiserror::Error;
11
12#[derive(Debug, Error)]
13pub enum CacheError {
14    #[error("serialize: {0}")]
15    Serialize(String),
16    #[error("deserialize: {0}")]
17    Deserialize(String),
18    #[error("{0}")]
19    Msg(String),
20}
21
22/// JSON get/set/remember on a [`KvStore`].
23#[derive(Clone)]
24pub struct Cache {
25    store: Arc<dyn KvStore>,
26}
27
28impl Cache {
29    pub fn new(store: Arc<dyn KvStore>) -> Self {
30        Self { store }
31    }
32
33    pub async fn get_json<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
34        let bytes = self.store.get(key).await?;
35        serde_json::from_slice(&bytes).ok()
36    }
37
38    pub async fn set_json<T: Serialize>(
39        &self,
40        key: &str,
41        val: &T,
42        ttl: Option<Duration>,
43    ) -> Result<(), CacheError> {
44        let bytes = serde_json::to_vec(val).map_err(|e| CacheError::Serialize(e.to_string()))?;
45        self.store.set(key, Bytes::from(bytes), ttl).await;
46        Ok(())
47    }
48
49    /// Return cached value or compute, store, and return.
50    pub async fn remember<T, F, Fut>(
51        &self,
52        key: &str,
53        ttl: Option<Duration>,
54        f: F,
55    ) -> Result<T, CacheError>
56    where
57        T: Serialize + DeserializeOwned,
58        F: FnOnce() -> Fut,
59        Fut: Future<Output = Result<T, CacheError>>,
60    {
61        if let Some(hit) = self.get_json::<T>(key).await {
62            return Ok(hit);
63        }
64        let val = f().await?;
65        self.set_json(key, &val, ttl).await?;
66        Ok(val)
67    }
68
69    pub async fn invalidate(&self, key: &str) {
70        self.store.remove(key).await;
71    }
72}
73
74impl AppStore {
75    /// Namespaced JSON cache (`cache:` prefix on the shared backend).
76    pub fn cache(&self) -> Cache {
77        Cache::new(Arc::new(namespace(Arc::clone(&self.inner), "cache")))
78    }
79}