1use std::any::Any;
18use std::cell::{Cell, Ref, RefCell};
19use std::collections::HashMap;
20use std::rc::Rc;
21
22#[derive(Debug, Clone)]
24pub struct TimelineEntry<T> {
25 pub event: T,
27 pub time_ms: u64,
29 pub source: String,
31 pub seq: u64,
33}
34
35#[derive(Clone)]
39pub struct Timeline<T: 'static> {
40 inner: Rc<RefCell<Vec<TimelineEntry<T>>>>,
41}
42
43impl<T: 'static> Timeline<T> {
44 pub fn len(&self) -> usize {
46 self.inner.borrow().len()
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.inner.borrow().is_empty()
52 }
53
54 pub fn all(&self) -> Ref<'_, Vec<TimelineEntry<T>>> {
56 self.inner.borrow()
57 }
58}
59
60impl<T: Clone + 'static> Timeline<T> {
61 pub fn since(&self, index: usize) -> Vec<TimelineEntry<T>> {
63 self.inner.borrow()[index..].to_vec()
64 }
65
66 pub fn last(&self) -> Option<TimelineEntry<T>> {
68 self.inner.borrow().last().cloned()
69 }
70}
71
72#[derive(Clone)]
77pub struct StateHandle {
78 inner: Rc<RefCell<HashMap<String, Box<dyn Any>>>>,
79 timelines: Rc<RefCell<HashMap<String, Box<dyn Any>>>>,
80 next_seq: Rc<Cell<u64>>,
81}
82
83impl Default for StateHandle {
84 fn default() -> Self {
85 Self {
86 inner: Rc::default(),
87 timelines: Rc::default(),
88 next_seq: Rc::new(Cell::new(0)),
89 }
90 }
91}
92
93impl StateHandle {
94 pub fn new() -> Self {
96 Self::default()
97 }
98
99 pub fn publish<T: Any + 'static>(&self, key: &str, value: T) {
101 self.inner
102 .borrow_mut()
103 .insert(key.to_string(), Box::new(value));
104 }
105
106 pub fn get<T: Any + Clone + 'static>(&self, key: &str) -> Option<T> {
110 self.inner
111 .borrow()
112 .get(key)
113 .and_then(|v| v.downcast_ref::<T>())
114 .cloned()
115 }
116
117 pub fn contains(&self, key: &str) -> bool {
119 self.inner.borrow().contains_key(key)
120 }
121
122 pub fn emit_raw<T: Any + 'static>(&self, key: &str, event: T, time_ms: u64, source: &str) {
126 let seq = self.next_seq.get();
127 self.next_seq.set(seq + 1);
128 let entry = TimelineEntry {
129 event,
130 time_ms,
131 source: source.to_string(),
132 seq,
133 };
134
135 let rc = {
138 let mut map = self.timelines.borrow_mut();
139 let boxed = map
140 .entry(key.to_string())
141 .or_insert_with(|| Box::new(Rc::new(RefCell::new(Vec::<TimelineEntry<T>>::new()))));
142 boxed
143 .downcast_ref::<Rc<RefCell<Vec<TimelineEntry<T>>>>>()
144 .expect("timeline type mismatch: key already used with a different type")
145 .clone()
146 };
147 rc.borrow_mut().push(entry);
148 }
149
150 pub fn timeline<T: Any + 'static>(&self, key: &str) -> Option<Timeline<T>> {
155 let map = self.timelines.borrow();
156 let boxed = map.get(key)?;
157 let rc = boxed.downcast_ref::<Rc<RefCell<Vec<TimelineEntry<T>>>>>()?;
158 Some(Timeline { inner: rc.clone() })
159 }
160}
161
162impl std::fmt::Debug for StateHandle {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 let state_count = self.inner.borrow().len();
165 let timeline_count = self.timelines.borrow().len();
166 f.debug_struct("StateHandle")
167 .field("state_keys", &state_count)
168 .field("timeline_keys", &timeline_count)
169 .finish()
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_publish_and_get() {
179 let state = StateHandle::new();
180 state.publish("count", 42u64);
181 assert_eq!(state.get::<u64>("count"), Some(42));
182 }
183
184 #[test]
185 fn test_get_wrong_type_returns_none() {
186 let state = StateHandle::new();
187 state.publish("count", 42u64);
188 assert_eq!(state.get::<String>("count"), None);
189 }
190
191 #[test]
192 fn test_get_missing_key_returns_none() {
193 let state = StateHandle::new();
194 assert_eq!(state.get::<u64>("missing"), None);
195 }
196
197 #[test]
198 fn test_contains() {
199 let state = StateHandle::new();
200 assert!(!state.contains("key"));
201 state.publish("key", "value".to_string());
202 assert!(state.contains("key"));
203 }
204
205 #[test]
206 fn test_publish_replaces() {
207 let state = StateHandle::new();
208 state.publish("x", 1u64);
209 state.publish("x", 2u64);
210 assert_eq!(state.get::<u64>("x"), Some(2));
211 }
212
213 #[test]
214 fn test_clone_shares_state() {
215 let state = StateHandle::new();
216 let clone = state.clone();
217 state.publish("shared", true);
218 assert_eq!(clone.get::<bool>("shared"), Some(true));
219 }
220
221 #[test]
222 fn test_emit_and_read_timeline() {
223 let state = StateHandle::new();
224 state.emit_raw("ops", "first", 100, "10.0.1.1");
225 state.emit_raw("ops", "second", 200, "10.0.1.2");
226 state.emit_raw("ops", "third", 300, "10.0.1.1");
227
228 let tl = state
229 .timeline::<&str>("ops")
230 .expect("timeline should exist");
231 assert_eq!(tl.len(), 3);
232
233 let entries = tl.all();
234 assert_eq!(entries[0].event, "first");
235 assert_eq!(entries[0].time_ms, 100);
236 assert_eq!(entries[0].source, "10.0.1.1");
237 assert_eq!(entries[0].seq, 0);
238 assert_eq!(entries[1].event, "second");
239 assert_eq!(entries[1].seq, 1);
240 assert_eq!(entries[2].event, "third");
241 assert_eq!(entries[2].seq, 2);
242 }
243
244 #[test]
245 fn test_timeline_missing_key_returns_none() {
246 let state = StateHandle::new();
247 assert!(state.timeline::<u64>("missing").is_none());
248 }
249
250 #[test]
251 fn test_timeline_wrong_type_returns_none() {
252 let state = StateHandle::new();
253 state.emit_raw("ops", 42u64, 0, "10.0.1.1");
254 assert!(state.timeline::<String>("ops").is_none());
255 }
256
257 #[test]
258 fn test_timeline_since() {
259 let state = StateHandle::new();
260 for i in 0..5u64 {
261 state.emit_raw("events", i, i * 10, "10.0.1.1");
262 }
263
264 let tl = state.timeline::<u64>("events").expect("should exist");
265 let tail = tl.since(3);
266 assert_eq!(tail.len(), 2);
267 assert_eq!(tail[0].event, 3);
268 assert_eq!(tail[1].event, 4);
269 }
270
271 #[test]
272 fn test_timeline_clone_shares_data() {
273 let state = StateHandle::new();
274 state.emit_raw("log", "a", 0, "10.0.1.1");
275
276 let tl = state.timeline::<&str>("log").expect("should exist");
277 let tl2 = tl.clone();
278
279 state.emit_raw("log", "b", 10, "10.0.1.2");
280 assert_eq!(tl.len(), 2);
281 assert_eq!(tl2.len(), 2);
282 }
283
284 #[test]
285 fn test_seq_spans_timelines() {
286 let state = StateHandle::new();
287 state.emit_raw("a", 1u64, 0, "10.0.1.1");
288 state.emit_raw("b", 2u64, 0, "10.0.1.1");
289
290 let tl_a = state.timeline::<u64>("a").expect("a");
291 let tl_b = state.timeline::<u64>("b").expect("b");
292 assert_eq!(tl_a.all()[0].seq, 0);
293 assert_eq!(tl_b.all()[0].seq, 1);
294 }
295
296 #[test]
297 fn test_timeline_last() {
298 let state = StateHandle::new();
299 state.emit_raw("log", "first", 0, "10.0.1.1");
300 state.emit_raw("log", "second", 10, "10.0.1.1");
301
302 let tl = state.timeline::<&str>("log").expect("should exist");
303 let last = tl.last().expect("should have last");
304 assert_eq!(last.event, "second");
305 assert_eq!(last.time_ms, 10);
306 }
307}