1use std::collections::HashMap;
2
3domain_event!(Event {
4 SessionStart,
5 SessionEnd,
6 UserPromptSubmit,
7 PreToolUse,
8 PostToolUse,
9 InferenceComplete,
10 OnError,
11 OnWarning,
12 DelegationStart,
13 DelegationComplete,
14});
15
16#[derive(Debug, Clone)]
18pub struct EventContext {
19 pub session_context: Option<String>,
20 pub user_message: Option<String>,
21 pub tool_name: Option<String>,
22 pub tool_output: Option<String>,
23 pub error: Option<String>,
24 pub warning: Option<String>,
25 pub metadata: HashMap<String, String>,
26}
27
28impl EventContext {
29 pub fn new() -> Self {
30 EventContext {
31 session_context: None,
32 user_message: None,
33 tool_name: None,
34 tool_output: None,
35 error: None,
36 warning: None,
37 metadata: HashMap::new(),
38 }
39 }
40
41 pub fn with_session_context(mut self, ctx: String) -> Self {
42 self.session_context = Some(ctx);
43 self
44 }
45
46 pub fn with_user_message(mut self, msg: String) -> Self {
47 self.user_message = Some(msg);
48 self
49 }
50
51 pub fn with_tool_name(mut self, name: String) -> Self {
52 self.tool_name = Some(name);
53 self
54 }
55
56 pub fn with_tool_output(mut self, output: String) -> Self {
57 self.tool_output = Some(output);
58 self
59 }
60
61 pub fn with_error(mut self, error: String) -> Self {
62 self.error = Some(error);
63 self
64 }
65
66 pub fn with_metadata(mut self, key: String, value: String) -> Self {
67 self.metadata.insert(key, value);
68 self
69 }
70}
71
72impl Default for EventContext {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78pub type EventHandler = Box<dyn Fn(Event, &EventContext) + Send + Sync>;
80
81pub struct EventManager {
83 handlers: HashMap<Event, Vec<EventHandler>>,
84}
85
86impl EventManager {
87 pub fn new() -> Self {
88 EventManager {
89 handlers: HashMap::new(),
90 }
91 }
92
93 pub fn on<F>(&mut self, event: Event, handler: F)
94 where
95 F: Fn(Event, &EventContext) + Send + Sync + 'static,
96 {
97 self.handlers
98 .entry(event)
99 .or_default()
100 .push(Box::new(handler));
101 }
102
103 pub fn fire(&self, event: Event, context: &EventContext) {
104 if let Some(handlers) = self.handlers.get(&event) {
105 for handler in handlers {
106 handler(event, context);
107 }
108 }
109 }
110
111 pub fn clear(&mut self, event: Event) {
112 self.handlers.remove(&event);
113 }
114}
115
116impl Default for EventManager {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use std::sync::{Arc, Mutex};
126
127 #[test]
128 fn event_names_from_macro() {
129 assert_eq!(Event::SessionStart.name(), "SessionStart");
130 assert_eq!(Event::SessionEnd.name(), "SessionEnd");
131 assert_eq!(Event::PreToolUse.name(), "PreToolUse");
132 assert_eq!(Event::OnError.name(), "OnError");
133 assert_eq!(Event::DelegationStart.name(), "DelegationStart");
134 }
135
136 #[test]
137 fn event_context_builder() {
138 let ctx = EventContext::new()
139 .with_session_context("session_data".to_string())
140 .with_user_message("test message".to_string())
141 .with_error("test error".to_string());
142
143 assert_eq!(ctx.session_context, Some("session_data".to_string()));
144 assert_eq!(ctx.user_message, Some("test message".to_string()));
145 assert_eq!(ctx.error, Some("test error".to_string()));
146 assert!(ctx.tool_name.is_none());
147 }
148
149 #[test]
150 fn event_context_metadata() {
151 let ctx = EventContext::new()
152 .with_metadata("key1".to_string(), "value1".to_string())
153 .with_metadata("key2".to_string(), "value2".to_string());
154
155 assert_eq!(ctx.metadata.get("key1"), Some(&"value1".to_string()));
156 assert_eq!(ctx.metadata.get("key2"), Some(&"value2".to_string()));
157 }
158
159 #[test]
160 fn event_manager_fire() {
161 let mut manager = EventManager::new();
162 let counter = Arc::new(Mutex::new(0));
163 let counter_clone = counter.clone();
164
165 manager.on(Event::SessionStart, move |_event, _ctx| {
166 let mut c = counter_clone.lock().unwrap();
167 *c += 1;
168 });
169
170 let ctx = EventContext::new();
171 manager.fire(Event::SessionStart, &ctx);
172
173 assert_eq!(*counter.lock().unwrap(), 1);
174 }
175
176 #[test]
177 fn event_manager_multiple_handlers() {
178 let mut manager = EventManager::new();
179 let counter1 = Arc::new(Mutex::new(0));
180 let counter1_clone = counter1.clone();
181 let counter2 = Arc::new(Mutex::new(0));
182 let counter2_clone = counter2.clone();
183
184 manager.on(Event::PreToolUse, move |_event, _ctx| {
185 let mut c = counter1_clone.lock().unwrap();
186 *c += 1;
187 });
188
189 manager.on(Event::PreToolUse, move |_event, _ctx| {
190 let mut c = counter2_clone.lock().unwrap();
191 *c += 1;
192 });
193
194 let ctx = EventContext::new();
195 manager.fire(Event::PreToolUse, &ctx);
196
197 assert_eq!(*counter1.lock().unwrap(), 1);
198 assert_eq!(*counter2.lock().unwrap(), 1);
199 }
200
201 #[test]
202 fn event_manager_clear() {
203 let mut manager = EventManager::new();
204 let counter = Arc::new(Mutex::new(0));
205 let counter_clone = counter.clone();
206
207 manager.on(Event::SessionStart, move |_event, _ctx| {
208 let mut c = counter_clone.lock().unwrap();
209 *c += 1;
210 });
211
212 manager.clear(Event::SessionStart);
213 let ctx = EventContext::new();
214 manager.fire(Event::SessionStart, &ctx);
215
216 assert_eq!(*counter.lock().unwrap(), 0);
217 }
218}