1use crate::store::CacheStore;
2use doido_core::Result;
3use serde_json::Value;
4use std::{
5 collections::HashMap,
6 sync::RwLock,
7 time::{Duration, Instant},
8};
9
10pub struct MemoryStore {
11 data: RwLock<HashMap<String, (Value, Option<Instant>)>>,
12}
13
14impl MemoryStore {
15 pub fn new() -> Self {
16 Self {
17 data: RwLock::new(HashMap::new()),
18 }
19 }
20}
21
22impl Default for MemoryStore {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28#[async_trait::async_trait]
29impl CacheStore for MemoryStore {
30 async fn get(&self, key: &str) -> Result<Option<Value>> {
31 let guard = self.data.read().unwrap();
32 match guard.get(key) {
33 None => Ok(None),
34 Some((val, None)) => Ok(Some(val.clone())),
35 Some((val, Some(expiry))) => {
36 if Instant::now() > *expiry {
37 drop(guard);
38 self.data.write().unwrap().remove(key);
39 Ok(None)
40 } else {
41 Ok(Some(val.clone()))
42 }
43 }
44 }
45 }
46
47 async fn set(&self, key: &str, value: Value, ttl_secs: Option<u64>) -> Result<()> {
48 let expiry = ttl_secs.map(|s| Instant::now() + Duration::from_secs(s));
49 self.data
50 .write()
51 .unwrap()
52 .insert(key.to_string(), (value, expiry));
53 Ok(())
54 }
55
56 async fn delete(&self, key: &str) -> Result<()> {
57 self.data.write().unwrap().remove(key);
58 Ok(())
59 }
60
61 async fn exists(&self, key: &str) -> Result<bool> {
62 Ok(self.get(key).await?.is_some())
63 }
64
65 async fn increment(&self, key: &str, by: i64) -> Result<i64> {
66 let mut data = self.data.write().unwrap();
67 let entry = data
68 .entry(key.to_string())
69 .or_insert((serde_json::json!(0), None));
70 let current = entry.0.as_i64().unwrap_or(0);
71 let new_val = current + by;
72 entry.0 = serde_json::json!(new_val);
73 Ok(new_val)
74 }
75
76 async fn decrement(&self, key: &str, by: i64) -> Result<i64> {
77 self.increment(key, -by).await
78 }
79
80 async fn clear(&self) -> Result<()> {
81 self.data.write().unwrap().clear();
82 Ok(())
83 }
84}