1use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13use std::time::{Duration, Instant};
14
15use async_trait::async_trait;
16use serde_json::Value;
17
18#[async_trait]
20pub trait State: Send + Sync {
21 async fn append(&self, key: &str, item: Value, max_len: Option<usize>, ttl: Option<Duration>);
25
26 async fn set(&self, key: &str, value: Value, ttl: Option<Duration>);
28
29 async fn get(&self, key: &str) -> Option<Value>;
31}
32
33pub struct NamespacedState {
36 inner: Arc<dyn State>,
37 prefix: String,
38}
39
40impl NamespacedState {
41 pub fn new(inner: Arc<dyn State>, namespace: impl std::fmt::Display) -> Self {
43 Self {
44 inner,
45 prefix: format!("{namespace}."),
46 }
47 }
48
49 pub fn prefix(&self) -> &str {
51 &self.prefix
52 }
53
54 fn key(&self, key: &str) -> String {
55 format!("{}{key}", self.prefix)
56 }
57}
58
59#[async_trait]
60impl State for NamespacedState {
61 async fn append(&self, key: &str, item: Value, max_len: Option<usize>, ttl: Option<Duration>) {
62 self.inner.append(&self.key(key), item, max_len, ttl).await;
63 }
64
65 async fn set(&self, key: &str, value: Value, ttl: Option<Duration>) {
66 self.inner.set(&self.key(key), value, ttl).await;
67 }
68
69 async fn get(&self, key: &str) -> Option<Value> {
70 self.inner.get(&self.key(key)).await
71 }
72}
73
74struct Entry {
75 value: Value,
76 expires_at: Option<Instant>,
77}
78
79impl Entry {
80 fn is_expired(&self, now: Instant) -> bool {
81 self.expires_at.map(|e| now >= e).unwrap_or(false)
82 }
83}
84
85#[derive(Default)]
87pub struct MemoryState {
88 map: Mutex<HashMap<String, Entry>>,
89}
90
91impl MemoryState {
92 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn from_snapshot(entries: serde_json::Map<String, Value>) -> Self {
100 Self {
101 map: Mutex::new(
102 entries
103 .into_iter()
104 .map(|(key, value)| {
105 (
106 key,
107 Entry {
108 value,
109 expires_at: None,
110 },
111 )
112 })
113 .collect(),
114 ),
115 }
116 }
117
118 pub fn snapshot(&self) -> serde_json::Map<String, Value> {
120 let now = Instant::now();
121 self.map
122 .lock()
123 .unwrap_or_else(std::sync::PoisonError::into_inner)
124 .iter()
125 .filter(|(_, entry)| !entry.is_expired(now))
126 .map(|(key, entry)| (key.clone(), entry.value.clone()))
127 .collect()
128 }
129
130 fn expiry(ttl: Option<Duration>) -> Option<Instant> {
131 ttl.map(|d| Instant::now() + d)
132 }
133}
134
135#[async_trait]
136impl State for MemoryState {
137 async fn append(&self, key: &str, item: Value, max_len: Option<usize>, ttl: Option<Duration>) {
138 let now = Instant::now();
139 let mut map = self
140 .map
141 .lock()
142 .unwrap_or_else(std::sync::PoisonError::into_inner);
143
144 if let Some(e) = map.get(key) {
146 if e.is_expired(now) {
147 map.remove(key);
148 }
149 }
150
151 let entry = map.entry(key.to_string()).or_insert_with(|| Entry {
152 value: Value::Array(Vec::new()),
153 expires_at: Self::expiry(ttl),
154 });
155
156 if !entry.value.is_array() {
157 entry.value = Value::Array(Vec::new());
158 }
159 if let Value::Array(list) = &mut entry.value {
160 list.push(item);
161 if let Some(cap) = max_len {
162 while list.len() > cap {
163 list.remove(0);
164 }
165 }
166 }
167 if ttl.is_some() {
168 entry.expires_at = Self::expiry(ttl);
169 }
170 }
171
172 async fn set(&self, key: &str, value: Value, ttl: Option<Duration>) {
173 let mut map = self
174 .map
175 .lock()
176 .unwrap_or_else(std::sync::PoisonError::into_inner);
177 map.insert(
178 key.to_string(),
179 Entry {
180 value,
181 expires_at: Self::expiry(ttl),
182 },
183 );
184 }
185
186 async fn get(&self, key: &str) -> Option<Value> {
187 let now = Instant::now();
188 let mut map = self
189 .map
190 .lock()
191 .unwrap_or_else(std::sync::PoisonError::into_inner);
192 match map.get(key) {
193 Some(e) if e.is_expired(now) => {
194 map.remove(key);
195 None
196 }
197 Some(e) => Some(e.value.clone()),
198 None => None,
199 }
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use serde_json::json;
207
208 #[tokio::test]
209 async fn append_respects_max_len() {
210 let s = MemoryState::new();
211 for i in 0..5 {
212 s.append("k", json!(i), Some(3), None).await;
213 }
214 let v = s.get("k").await.unwrap();
215 assert_eq!(v, json!([2, 3, 4]));
216 }
217
218 #[tokio::test]
219 async fn set_and_get() {
220 let s = MemoryState::new();
221 s.set("k", json!({"a": 1}), None).await;
222 assert_eq!(s.get("k").await.unwrap(), json!({"a": 1}));
223 assert!(s.get("missing").await.is_none());
224 }
225
226 #[tokio::test]
227 async fn namespaces_isolate_instances() {
228 let shared: Arc<dyn State> = Arc::new(MemoryState::new());
229 let first = NamespacedState::new(shared.clone(), "first");
230 let second = NamespacedState::new(shared, "second");
231 first.set("branch.value", json!(1), None).await;
232 second.set("branch.value", json!(2), None).await;
233 assert_eq!(first.get("branch.value").await, Some(json!(1)));
234 assert_eq!(second.get("branch.value").await, Some(json!(2)));
235 }
236}