rovenue/cache/
offerings.rs

1use rusqlite::params;
2
3use crate::error::RovenueResult;
4
5use super::CacheStore;
6
7/// Persistent store for the last-known offerings response, mirroring
8/// [`EtagRepo`](super::etag::EtagRepo). The cached `body` is the raw
9/// `OfferingsResponse` JSON (the `data` payload), keyed by a stable resource
10/// name so it can be parsed back and remapped when the network is unavailable.
11pub struct OfferingsCacheRepo<'a> {
12    store: &'a CacheStore,
13}
14
15impl<'a> OfferingsCacheRepo<'a> {
16    pub fn new(store: &'a CacheStore) -> Self {
17        Self { store }
18    }
19
20    pub fn get(&self, resource: &str) -> RovenueResult<Option<String>> {
21        self.store.with_conn(|c| {
22            let mut stmt = c.prepare("SELECT body FROM offerings_cache WHERE resource = ?1")?;
23            let mut rows = stmt.query(params![resource])?;
24            if let Some(r) = rows.next()? {
25                Ok(Some(r.get::<_, String>(0)?))
26            } else {
27                Ok(None)
28            }
29        })
30    }
31
32    pub fn put(&self, resource: &str, body: &str, updated_at_ms: u64) -> RovenueResult<()> {
33        self.store.with_conn(|c| {
34            c.execute(
35                "INSERT INTO offerings_cache (resource, body, updated_at_ms)
36                 VALUES (?1, ?2, ?3)
37                 ON CONFLICT(resource) DO UPDATE SET
38                   body = excluded.body,
39                   updated_at_ms = excluded.updated_at_ms",
40                params![resource, body, updated_at_ms as i64],
41            )?;
42            Ok(())
43        })
44    }
45}