1use chrono::{DateTime, Utc};
7use parking_lot::Mutex;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct StateTransition {
15 pub key: String,
16 pub old_value: Option<Value>,
17 pub new_value: Option<Value>,
18 pub action_id: String,
19 pub timestamp: DateTime<Utc>,
20}
21
22pub struct StateStore {
27 state: Mutex<HashMap<String, Value>>,
28 transitions: Mutex<Vec<StateTransition>>,
29}
30
31impl StateStore {
32 pub fn new() -> Self {
33 Self {
34 state: Mutex::new(HashMap::new()),
35 transitions: Mutex::new(Vec::new()),
36 }
37 }
38
39 pub fn get(&self, key: &str) -> Option<Value> {
40 self.state.lock().get(key).cloned()
41 }
42
43 pub fn get_or(&self, key: &str, default: Value) -> Value {
44 self.state.lock().get(key).cloned().unwrap_or(default)
45 }
46
47 pub fn exists(&self, key: &str) -> bool {
48 self.state.lock().contains_key(key)
49 }
50
51 pub fn set(&self, key: &str, value: Value, action_id: &str) -> StateTransition {
52 let mut state = self.state.lock();
53 let old = state.get(key).cloned();
54 state.insert(key.to_string(), value.clone());
55
56 let t = StateTransition {
57 key: key.to_string(),
58 old_value: old,
59 new_value: Some(value),
60 action_id: action_id.to_string(),
61 timestamp: Utc::now(),
62 };
63
64 self.transitions.lock().push(t.clone());
65 t
66 }
67
68 pub fn delete(&self, key: &str, action_id: &str) -> Option<StateTransition> {
69 let mut state = self.state.lock();
70 let old = state.remove(key)?;
71
72 let t = StateTransition {
73 key: key.to_string(),
74 old_value: Some(old),
75 new_value: None,
76 action_id: action_id.to_string(),
77 timestamp: Utc::now(),
78 };
79
80 self.transitions.lock().push(t.clone());
81 Some(t)
82 }
83
84 pub fn snapshot(&self) -> HashMap<String, Value> {
86 self.state.lock().clone()
87 }
88
89 pub fn restore(&self, snapshot: HashMap<String, Value>, transition_count: usize) {
91 *self.state.lock() = snapshot;
92 self.transitions.lock().truncate(transition_count);
93 }
94
95 pub fn transition_count(&self) -> usize {
96 self.transitions.lock().len()
97 }
98
99 pub fn transitions(&self) -> Vec<StateTransition> {
100 self.transitions.lock().clone()
101 }
102
103 pub fn transitions_since(&self, index: usize) -> Vec<StateTransition> {
104 let transitions = self.transitions.lock();
105 let start = index.min(transitions.len());
106 transitions[start..].to_vec()
107 }
108
109 pub fn keys(&self) -> Vec<String> {
110 self.state.lock().keys().cloned().collect()
111 }
112
113 pub fn replace_all(&self, snapshot: HashMap<String, Value>) {
118 *self.state.lock() = snapshot;
119 self.transitions.lock().clear();
120 }
121}
122
123impl Default for StateStore {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl car_ir::precondition::StateView for StateStore {
130 fn get_value(&self, key: &str) -> Option<Value> {
131 self.get(key)
132 }
133 fn key_exists(&self, key: &str) -> bool {
134 self.exists(key)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn set_and_get() {
144 let store = StateStore::new();
145 store.set("x", Value::from(42), "test");
146 assert_eq!(store.get("x"), Some(Value::from(42)));
147 }
148
149 #[test]
150 fn exists() {
151 let store = StateStore::new();
152 assert!(!store.exists("x"));
153 store.set("x", Value::from(1), "test");
154 assert!(store.exists("x"));
155 }
156
157 #[test]
158 fn delete() {
159 let store = StateStore::new();
160 store.set("x", Value::from(1), "test");
161 let t = store.delete("x", "test");
162 assert!(t.is_some());
163 assert!(!store.exists("x"));
164 }
165
166 #[test]
167 fn delete_nonexistent() {
168 let store = StateStore::new();
169 assert!(store.delete("x", "test").is_none());
170 }
171
172 #[test]
173 fn snapshot_and_restore() {
174 let store = StateStore::new();
175 store.set("x", Value::from(1), "a");
176 let snap = store.snapshot();
177 let tc = store.transition_count();
178
179 store.set("y", Value::from(2), "b");
180 assert!(store.exists("y"));
181
182 store.restore(snap, tc);
183 assert!(store.exists("x"));
184 assert!(!store.exists("y"));
185 assert_eq!(store.transition_count(), 1);
186 }
187
188 #[test]
189 fn transitions_logged() {
190 let store = StateStore::new();
191 store.set("a", Value::from(1), "act1");
192 store.set("b", Value::from(2), "act2");
193
194 let transitions = store.transitions();
195 assert_eq!(transitions.len(), 2);
196 assert_eq!(transitions[0].key, "a");
197 assert_eq!(transitions[1].key, "b");
198 }
199
200 #[test]
201 fn transitions_since() {
202 let store = StateStore::new();
203 store.set("a", Value::from(1), "act1");
204 let idx = store.transition_count();
205 store.set("b", Value::from(2), "act2");
206
207 let since = store.transitions_since(idx);
208 assert_eq!(since.len(), 1);
209 assert_eq!(since[0].key, "b");
210 }
211
212 #[test]
213 fn transition_records_old_value() {
214 let store = StateStore::new();
215 store.set("x", Value::from(1), "first");
216 store.set("x", Value::from(2), "second");
217
218 let transitions = store.transitions();
219 assert_eq!(transitions[1].old_value, Some(Value::from(1)));
220 assert_eq!(transitions[1].new_value, Some(Value::from(2)));
221 }
222
223 #[test]
224 fn keys() {
225 let store = StateStore::new();
226 store.set("a", Value::from(1), "t");
227 store.set("b", Value::from(2), "t");
228 let mut keys = store.keys();
229 keys.sort();
230 assert_eq!(keys, vec!["a", "b"]);
231 }
232
233 #[test]
234 fn transitions_since_after_restore_does_not_panic() {
235 let store = StateStore::new();
236 store.set("a", serde_json::json!(1), "test");
237 store.set("b", serde_json::json!(2), "test");
238 let count_before = store.transition_count(); store.restore(HashMap::new(), 0);
242
243 let result = store.transitions_since(count_before);
245 assert!(result.is_empty());
246 }
247
248 #[test]
249 fn transitions_since_normal_usage() {
250 let store = StateStore::new();
251 store.set("a", serde_json::json!(1), "test");
252 let mark = store.transition_count();
253 store.set("b", serde_json::json!(2), "test");
254 let since = store.transitions_since(mark);
255 assert_eq!(since.len(), 1);
256 assert_eq!(since[0].key, "b");
257 }
258
259 #[test]
260 fn replace_all_swaps_state_without_transitions() {
261 let store = StateStore::new();
262 store.set("old_key", serde_json::json!("old"), "setup");
263
264 let mut new_state = HashMap::new();
265 new_state.insert("new_key".to_string(), serde_json::json!("new"));
266 store.replace_all(new_state);
267
268 assert_eq!(store.get("new_key"), Some(serde_json::json!("new")));
269 assert_eq!(store.get("old_key"), None);
270 assert_eq!(store.transition_count(), 0);
272 }
273}