1use std::any::Any;
14use std::fmt::Debug;
15
16use chrono::{DateTime, Utc};
17use serde::{Deserialize, Serialize};
18use uuid::Uuid;
19
20pub trait Event: Send + Sync + Debug + Clone + 'static {
30 fn event_type() -> &'static str
35 where
36 Self: Sized;
37
38 fn event_type_id(&self) -> &'static str;
40
41 fn as_any(&self) -> &dyn Any;
43
44 fn clone_boxed(&self) -> Box<dyn AnyEvent>;
46
47 #[must_use]
49 fn to_json(&self) -> serde_json::Value;
50}
51
52pub trait AnyEvent: Send + Sync + Debug {
57 fn event_type_id(&self) -> &'static str;
59
60 fn as_any(&self) -> &dyn Any;
62
63 fn clone_boxed(&self) -> Box<dyn AnyEvent>;
65
66 #[must_use]
68 fn to_json(&self) -> serde_json::Value;
69}
70
71impl<T> AnyEvent for T
73where
74 T: Event + Serialize,
75{
76 fn event_type_id(&self) -> &'static str {
77 Event::event_type_id(self)
78 }
79
80 fn as_any(&self) -> &dyn Any {
81 Event::as_any(self)
82 }
83
84 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
85 Event::clone_boxed(self)
86 }
87
88 fn to_json(&self) -> serde_json::Value {
89 Event::to_json(self)
90 }
91}
92
93impl dyn AnyEvent {
95 #[must_use]
100 pub fn downcast_ref<T: Event>(&self) -> Option<&T> {
101 self.as_any().downcast_ref::<T>()
102 }
103}
104
105impl Clone for Box<dyn AnyEvent> {
107 fn clone(&self) -> Self {
108 self.clone_boxed()
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct StartEvent {
119 pub data: serde_json::Value,
121}
122
123impl Event for StartEvent {
124 fn event_type() -> &'static str {
125 static REGISTER: std::sync::Once = std::sync::Once::new();
126 REGISTER.call_once(|| {
127 register_event_deserializer("blazen::StartEvent", |value| {
128 serde_json::from_value::<StartEvent>(value)
129 .ok()
130 .map(|e| Box::new(e) as _)
131 });
132 });
133 "blazen::StartEvent"
134 }
135
136 fn event_type_id(&self) -> &'static str {
137 "blazen::StartEvent"
138 }
139
140 fn as_any(&self) -> &dyn Any {
141 self
142 }
143
144 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
145 Box::new(self.clone())
146 }
147
148 fn to_json(&self) -> serde_json::Value {
149 serde_json::to_value(self).expect("StartEvent serialization should never fail")
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct StopEvent {
156 pub result: serde_json::Value,
158}
159
160impl Event for StopEvent {
161 fn event_type() -> &'static str {
162 static REGISTER: std::sync::Once = std::sync::Once::new();
163 REGISTER.call_once(|| {
164 register_event_deserializer("blazen::StopEvent", |value| {
165 serde_json::from_value::<StopEvent>(value)
166 .ok()
167 .map(|e| Box::new(e) as _)
168 });
169 });
170 "blazen::StopEvent"
171 }
172
173 fn event_type_id(&self) -> &'static str {
174 "blazen::StopEvent"
175 }
176
177 fn as_any(&self) -> &dyn Any {
178 self
179 }
180
181 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
182 Box::new(self.clone())
183 }
184
185 fn to_json(&self) -> serde_json::Value {
186 serde_json::to_value(self).expect("StopEvent serialization should never fail")
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct InputRequestEvent {
193 pub request_id: String,
195 pub prompt: String,
197 pub metadata: serde_json::Value,
199}
200
201impl Event for InputRequestEvent {
202 fn event_type() -> &'static str {
203 static REGISTER: std::sync::Once = std::sync::Once::new();
204 REGISTER.call_once(|| {
205 register_event_deserializer("blazen::InputRequestEvent", |value| {
206 serde_json::from_value::<InputRequestEvent>(value)
207 .ok()
208 .map(|e| Box::new(e) as _)
209 });
210 });
211 "blazen::InputRequestEvent"
212 }
213
214 fn event_type_id(&self) -> &'static str {
215 "blazen::InputRequestEvent"
216 }
217
218 fn as_any(&self) -> &dyn Any {
219 self
220 }
221
222 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
223 Box::new(self.clone())
224 }
225
226 fn to_json(&self) -> serde_json::Value {
227 serde_json::to_value(self).expect("InputRequestEvent serialization should never fail")
228 }
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct InputResponseEvent {
234 pub request_id: String,
236 pub response: serde_json::Value,
238}
239
240impl Event for InputResponseEvent {
241 fn event_type() -> &'static str {
242 static REGISTER: std::sync::Once = std::sync::Once::new();
243 REGISTER.call_once(|| {
244 register_event_deserializer("blazen::InputResponseEvent", |value| {
245 serde_json::from_value::<InputResponseEvent>(value)
246 .ok()
247 .map(|e| Box::new(e) as _)
248 });
249 });
250 "blazen::InputResponseEvent"
251 }
252
253 fn event_type_id(&self) -> &'static str {
254 "blazen::InputResponseEvent"
255 }
256
257 fn as_any(&self) -> &dyn Any {
258 self
259 }
260
261 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
262 Box::new(self.clone())
263 }
264
265 fn to_json(&self) -> serde_json::Value {
266 serde_json::to_value(self).expect("InputResponseEvent serialization should never fail")
267 }
268}
269
270#[derive(Debug)]
279pub struct EventEnvelope {
280 pub event: Box<dyn AnyEvent>,
282 pub source_step: Option<String>,
284 pub timestamp: DateTime<Utc>,
286 pub id: Uuid,
288}
289
290impl EventEnvelope {
291 #[must_use]
293 pub fn new(event: Box<dyn AnyEvent>, source_step: Option<String>) -> Self {
294 Self {
295 event,
296 source_step,
297 timestamp: Utc::now(),
298 id: Uuid::new_v4(),
299 }
300 }
301}
302
303pub type EventDeserializerFn = fn(serde_json::Value) -> Option<Box<dyn AnyEvent>>;
309
310static EVENT_DESERIALIZER_REGISTRY: std::sync::LazyLock<
317 dashmap::DashMap<&'static str, EventDeserializerFn>,
318> = std::sync::LazyLock::new(dashmap::DashMap::new);
319
320pub fn register_event_deserializer(event_type: &'static str, deserializer: EventDeserializerFn) {
325 EVENT_DESERIALIZER_REGISTRY.insert(event_type, deserializer);
326}
327
328pub fn try_deserialize_event(
334 event_type: &str,
335 data: &serde_json::Value,
336) -> Option<Box<dyn AnyEvent>> {
337 let entry = EVENT_DESERIALIZER_REGISTRY.get(event_type)?;
338 let deserializer = *entry.value();
339 deserializer(data.clone())
340}
341
342static EVENT_TYPE_REGISTRY: std::sync::LazyLock<dashmap::DashMap<String, &'static str>> =
352 std::sync::LazyLock::new(dashmap::DashMap::new);
353
354pub fn intern_event_type(name: &str) -> &'static str {
360 if let Some(entry) = EVENT_TYPE_REGISTRY.get(name) {
361 return entry.value();
362 }
363 let leaked: &'static str = Box::leak(name.to_string().into_boxed_str());
364 EVENT_TYPE_REGISTRY.insert(name.to_string(), leaked);
365 leaked
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct DynamicEvent {
378 pub event_type: String,
380 pub data: serde_json::Value,
382}
383
384impl Event for DynamicEvent {
385 fn event_type() -> &'static str
386 where
387 Self: Sized,
388 {
389 "dynamic"
393 }
394
395 fn event_type_id(&self) -> &'static str {
396 intern_event_type(&self.event_type)
397 }
398
399 fn as_any(&self) -> &dyn Any {
400 self
401 }
402
403 fn clone_boxed(&self) -> Box<dyn AnyEvent> {
404 Box::new(self.clone())
405 }
406
407 fn to_json(&self) -> serde_json::Value {
408 self.data.clone()
409 }
410}
411
412#[cfg(test)]
417mod tests {
418 use super::*;
419
420 #[test]
421 fn start_event_type_id() {
422 assert_eq!(StartEvent::event_type(), "blazen::StartEvent");
423 let evt = StartEvent {
424 data: serde_json::json!({"key": "value"}),
425 };
426 assert_eq!(Event::event_type_id(&evt), "blazen::StartEvent");
427 }
428
429 #[test]
430 fn stop_event_type_id() {
431 assert_eq!(StopEvent::event_type(), "blazen::StopEvent");
432 let evt = StopEvent {
433 result: serde_json::json!(42),
434 };
435 assert_eq!(Event::event_type_id(&evt), "blazen::StopEvent");
436 }
437
438 #[test]
439 fn any_event_downcast() {
440 let evt = StartEvent {
441 data: serde_json::json!(null),
442 };
443 let boxed: Box<dyn AnyEvent> = Box::new(evt.clone());
444 let downcasted = boxed.downcast_ref::<StartEvent>().unwrap();
445 assert_eq!(downcasted.data, evt.data);
446
447 assert!(boxed.downcast_ref::<StopEvent>().is_none());
449 }
450
451 #[test]
452 fn clone_boxed_any_event() {
453 let evt = StopEvent {
454 result: serde_json::json!("done"),
455 };
456 let boxed: Box<dyn AnyEvent> = Box::new(evt);
457 let cloned = boxed.clone();
458 assert_eq!(boxed.event_type_id(), cloned.event_type_id());
459 assert_eq!(boxed.to_json(), cloned.to_json());
460 }
461
462 #[test]
463 fn event_envelope_constructor() {
464 let evt = StartEvent {
465 data: serde_json::json!({"hello": "world"}),
466 };
467 let envelope = EventEnvelope::new(Box::new(evt), Some("my_step".to_string()));
468 assert_eq!(envelope.source_step.as_deref(), Some("my_step"));
469 assert_eq!(envelope.event.event_type_id(), "blazen::StartEvent");
470 }
471
472 #[test]
473 fn to_json_roundtrip() {
474 let start = StartEvent {
475 data: serde_json::json!({"nums": [1, 2, 3]}),
476 };
477 let json = Event::to_json(&start);
478 let deserialized: StartEvent = serde_json::from_value(json).unwrap();
479 assert_eq!(start.data, deserialized.data);
480
481 let stop = StopEvent {
482 result: serde_json::json!("ok"),
483 };
484 let json = Event::to_json(&stop);
485 let deserialized: StopEvent = serde_json::from_value(json).unwrap();
486 assert_eq!(stop.result, deserialized.result);
487 }
488
489 #[test]
490 fn intern_event_type_returns_same_pointer() {
491 let a = intern_event_type("TestEventInEvents");
492 let b = intern_event_type("TestEventInEvents");
493 assert!(std::ptr::eq(a, b));
494 }
495
496 #[test]
497 fn dynamic_event_roundtrip() {
498 let evt = DynamicEvent {
499 event_type: "MyEvent".to_owned(),
500 data: serde_json::json!({"key": "value"}),
501 };
502 let json = Event::to_json(&evt);
503 assert_eq!(json["key"], "value");
505 }
506
507 #[test]
508 fn dynamic_event_type_id() {
509 let evt = DynamicEvent {
510 event_type: "CustomEvent".to_owned(),
511 data: serde_json::json!({}),
512 };
513 assert_eq!(Event::event_type_id(&evt), "CustomEvent");
514 }
515
516 #[test]
517 fn input_request_event_type_id() {
518 assert_eq!(InputRequestEvent::event_type(), "blazen::InputRequestEvent");
519 let evt = InputRequestEvent {
520 request_id: "req-1".to_string(),
521 prompt: "What is your name?".to_string(),
522 metadata: serde_json::json!({"choices": ["Alice", "Bob"]}),
523 };
524 assert_eq!(Event::event_type_id(&evt), "blazen::InputRequestEvent");
525 }
526
527 #[test]
528 fn input_response_event_type_id() {
529 assert_eq!(
530 InputResponseEvent::event_type(),
531 "blazen::InputResponseEvent"
532 );
533 let evt = InputResponseEvent {
534 request_id: "req-1".to_string(),
535 response: serde_json::json!("Alice"),
536 };
537 assert_eq!(Event::event_type_id(&evt), "blazen::InputResponseEvent");
538 }
539
540 #[test]
541 fn input_request_event_roundtrip() {
542 let evt = InputRequestEvent {
543 request_id: "req-42".to_string(),
544 prompt: "Pick a number".to_string(),
545 metadata: serde_json::json!({"min": 1, "max": 100}),
546 };
547 let json = Event::to_json(&evt);
548 let deserialized: InputRequestEvent = serde_json::from_value(json).unwrap();
549 assert_eq!(evt.request_id, deserialized.request_id);
550 assert_eq!(evt.prompt, deserialized.prompt);
551 assert_eq!(evt.metadata, deserialized.metadata);
552 }
553
554 #[test]
555 fn input_response_event_roundtrip() {
556 let evt = InputResponseEvent {
557 request_id: "req-42".to_string(),
558 response: serde_json::json!(77),
559 };
560 let json = Event::to_json(&evt);
561 let deserialized: InputResponseEvent = serde_json::from_value(json).unwrap();
562 assert_eq!(evt.request_id, deserialized.request_id);
563 assert_eq!(evt.response, deserialized.response);
564 }
565
566 #[test]
567 fn input_request_event_downcast() {
568 let evt = InputRequestEvent {
569 request_id: "req-99".to_string(),
570 prompt: "Confirm?".to_string(),
571 metadata: serde_json::json!(null),
572 };
573 let boxed: Box<dyn AnyEvent> = Box::new(evt.clone());
574 let downcasted = boxed.downcast_ref::<InputRequestEvent>().unwrap();
575 assert_eq!(downcasted.request_id, evt.request_id);
576
577 assert!(boxed.downcast_ref::<InputResponseEvent>().is_none());
579 }
580
581 #[test]
582 fn input_response_event_downcast() {
583 let evt = InputResponseEvent {
584 request_id: "req-99".to_string(),
585 response: serde_json::json!({"answer": true}),
586 };
587 let boxed: Box<dyn AnyEvent> = Box::new(evt.clone());
588 let downcasted = boxed.downcast_ref::<InputResponseEvent>().unwrap();
589 assert_eq!(downcasted.request_id, evt.request_id);
590
591 assert!(boxed.downcast_ref::<InputRequestEvent>().is_none());
593 }
594}