1use crate::response::ErgonomicResponse;
2use crate::traits::{Handled, Handler, HandlerError};
3use crate::{CanonicalMessage, MessageContext};
4use async_trait::async_trait;
5use futures::future::BoxFuture;
6use serde::de::DeserializeOwned;
7use std::collections::HashMap;
8use std::future::Future;
9use std::sync::Arc;
10
11#[derive(Clone)]
30pub struct TypeHandler {
31 pub(crate) handlers: HashMap<String, Arc<dyn Handler>>,
32 pub(crate) type_key: String, pub(crate) fallback: Option<Arc<dyn Handler>>,
34}
35
36pub const KIND_KEY: &str = "kind";
37
38pub trait IntoTypedHandler<T, Args>: Send + Sync + 'static {
40 type Future: Future<Output = Result<Handled, HandlerError>> + Send + 'static;
41 fn call(&self, msg: T, ctx: MessageContext) -> Self::Future;
42}
43
44impl<F, Fut, T> IntoTypedHandler<T, (T, Result<Handled, HandlerError>)> for F
47where
48 T: DeserializeOwned + Send + Sync + 'static,
49 F: Fn(T) -> Fut + Send + Sync + 'static,
50 Fut: Future<Output = Result<Handled, HandlerError>> + Send + 'static,
51{
52 type Future = Fut;
53 fn call(&self, msg: T, _ctx: MessageContext) -> Self::Future {
54 (self)(msg)
55 }
56}
57
58impl<F, Fut, T> IntoTypedHandler<T, (T, MessageContext, Result<Handled, HandlerError>)> for F
59where
60 T: DeserializeOwned + Send + Sync + 'static,
61 F: Fn(T, MessageContext) -> Fut + Send + Sync + 'static,
62 Fut: Future<Output = Result<Handled, HandlerError>> + Send + 'static,
63{
64 type Future = Fut;
65 fn call(&self, msg: T, ctx: MessageContext) -> Self::Future {
66 (self)(msg, ctx)
67 }
68}
69
70impl<F, Fut, T, R> IntoTypedHandler<T, (T, R)> for F
72where
73 T: DeserializeOwned + Send + Sync + 'static,
74 R: ErgonomicResponse,
75 F: Fn(T) -> Fut + Send + Sync + 'static,
76 Fut: Future<Output = R> + Send + 'static,
77{
78 type Future = BoxFuture<'static, Result<Handled, HandlerError>>;
79 fn call(&self, msg: T, _ctx: MessageContext) -> Self::Future {
80 let fut = (self)(msg);
81 Box::pin(async move { fut.await.into_handler_result() })
82 }
83}
84
85impl<F, Fut, T, R> IntoTypedHandler<T, (T, MessageContext, R)> for F
86where
87 T: DeserializeOwned + Send + Sync + 'static,
88 R: ErgonomicResponse,
89 F: Fn(T, MessageContext) -> Fut + Send + Sync + 'static,
90 Fut: Future<Output = R> + Send + 'static,
91{
92 type Future = BoxFuture<'static, Result<Handled, HandlerError>>;
93 fn call(&self, msg: T, ctx: MessageContext) -> Self::Future {
94 let fut = (self)(msg, ctx);
95 Box::pin(async move { fut.await.into_handler_result() })
96 }
97}
98
99impl Default for TypeHandler {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl TypeHandler {
106 pub fn new() -> Self {
108 Self {
109 handlers: HashMap::new(),
110 type_key: KIND_KEY.into(),
111 fallback: None,
112 }
113 }
114
115 pub fn add_handler(mut self, type_name: &str, handler: impl Handler + 'static) -> Self {
117 self.handlers
118 .insert(type_name.to_string(), Arc::new(handler));
119 self
120 }
121
122 pub fn with_fallback(mut self, handler: Arc<dyn Handler>) -> Self {
124 self.fallback = Some(handler);
125 self
126 }
127
128 #[doc(hidden)]
129 pub fn add_simple<T, H, Args>(self, type_name: &str, handler: H) -> Self
130 where
131 T: DeserializeOwned + Send + Sync + 'static,
132 H: IntoTypedHandler<T, Args>,
133 Args: Send + Sync + 'static,
134 {
135 self.add(type_name, handler)
136 }
137
138 pub fn add<T, H, Args>(mut self, type_name: &str, handler: H) -> Self
144 where
145 T: DeserializeOwned + Send + Sync + 'static,
146 H: IntoTypedHandler<T, Args>,
147 Args: Send + Sync + 'static,
148 {
149 let handler = Arc::new(handler);
150 let wrapper = move |msg: CanonicalMessage| {
151 let handler = handler.clone();
152 async move {
153 let data = msg.parse::<T>().map_err(|e| {
154 HandlerError::NonRetryable(anyhow::anyhow!("Deserialization failed: {}", e))
155 })?;
156 let ctx = MessageContext::from(msg);
157 handler.call(data, ctx).await
158 }
159 };
160 self.handlers
161 .insert(type_name.to_string(), Arc::new(wrapper));
162 self
163 }
164}
165
166#[async_trait]
167impl Handler for TypeHandler {
168 async fn handle(&self, msg: CanonicalMessage) -> Result<Handled, HandlerError> {
169 if let Some(type_val) = msg.metadata.get(&self.type_key) {
170 if let Some(handler) = self.handlers.get(type_val) {
171 return handler.handle(msg).await;
172 }
173 }
174
175 if let Some(fallback) = &self.fallback {
176 return fallback.handle(msg).await;
177 }
178
179 Err(HandlerError::NonRetryable(anyhow::anyhow!(
180 "No handler registered for type: '{:?}' and no fallback provided",
181 msg.metadata.get(&self.type_key)
182 )))
183 }
184
185 async fn handle_many(&self, msgs: Vec<CanonicalMessage>) -> Vec<Result<Handled, HandlerError>> {
186 #[derive(Clone, PartialEq)]
187 enum BatchTarget {
188 Handler(String),
189 Fallback,
190 }
191
192 let mut results: Vec<Option<Result<Handled, HandlerError>>> =
193 std::iter::repeat_with(|| None).take(msgs.len()).collect();
194 let mut runs: Vec<(BatchTarget, Vec<(usize, CanonicalMessage)>)> = Vec::new();
195
196 for (index, msg) in msgs.into_iter().enumerate() {
197 let target = if let Some(type_val) = msg.metadata.get(&self.type_key) {
198 if self.handlers.contains_key(type_val) {
199 Some(BatchTarget::Handler(type_val.clone()))
200 } else if self.fallback.is_some() {
201 Some(BatchTarget::Fallback)
202 } else {
203 None
204 }
205 } else if self.fallback.is_some() {
206 Some(BatchTarget::Fallback)
207 } else {
208 None
209 };
210
211 if let Some(target) = target {
212 if let Some((last_target, group)) = runs.last_mut() {
213 if *last_target == target {
214 group.push((index, msg));
215 continue;
216 }
217 }
218 runs.push((target, vec![(index, msg)]));
219 } else {
220 results[index] = Some(Err(HandlerError::NonRetryable(anyhow::anyhow!(
221 "No handler registered for type: '{:?}' and no fallback provided",
222 msg.metadata.get(&self.type_key)
223 ))));
224 }
225 }
226
227 for (target, group) in runs {
228 let expected = group.len();
229 let (indices, messages): (Vec<_>, Vec<_>) = group.into_iter().unzip();
230 let (handler, label) = match target {
231 BatchTarget::Handler(type_name) => {
232 let Some(handler) = self.handlers.get(&type_name) else {
233 for index in indices {
234 results[index] = Some(Err(HandlerError::NonRetryable(
235 anyhow::anyhow!("Handler batch dispatch did not produce a result"),
236 )));
237 }
238 continue;
239 };
240 (handler, format!("Handler for type '{type_name}'"))
241 }
242 BatchTarget::Fallback => {
243 let Some(handler) = &self.fallback else {
244 for index in indices {
245 results[index] = Some(Err(HandlerError::NonRetryable(
246 anyhow::anyhow!("Handler batch dispatch did not produce a result"),
247 )));
248 }
249 continue;
250 };
251 (handler, "Fallback handler".to_string())
252 }
253 };
254
255 let group_results = handler.handle_many(messages).await;
256
257 if group_results.len() != expected {
258 for index in indices {
259 results[index] = Some(Err(HandlerError::NonRetryable(anyhow::anyhow!(
260 "{} returned {} results for {} messages",
261 label,
262 group_results.len(),
263 expected
264 ))));
265 }
266 continue;
267 }
268
269 for (index, result) in indices.into_iter().zip(group_results) {
270 results[index] = Some(result);
271 }
272 }
273
274 results
275 .into_iter()
276 .map(|result| {
277 result.unwrap_or_else(|| {
278 Err(HandlerError::NonRetryable(anyhow::anyhow!(
279 "Handler batch dispatch did not produce a result"
280 )))
281 })
282 })
283 .collect()
284 }
285
286 fn register_handler(
287 &self,
288 type_name: &str,
289 handler: Arc<dyn Handler>,
290 ) -> Option<Arc<dyn Handler>> {
291 let mut th = self.clone();
292 th.handlers.insert(type_name.to_string(), handler);
293 Some(Arc::new(th))
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::msg;
301 use serde::{Deserialize, Serialize};
302 use std::sync::{Arc, Mutex};
303
304 #[derive(Serialize, Deserialize)]
305 struct TestMsg {
306 val: String,
307 }
308
309 #[tokio::test]
310 async fn test_typed_handler_dispatch() {
311 let handler = TypeHandler::new().add("test_a", |msg: TestMsg| async move {
312 assert_eq!(msg.val, "hello");
313 Ok(Handled::Ack)
314 });
315
316 let msg = msg!(
317 &TestMsg {
318 val: "hello".into(),
319 },
320 "test_a"
321 );
322
323 let res = handler.handle(msg).await;
324 assert!(res.is_ok());
325 }
326
327 #[tokio::test]
328 async fn test_typed_handler_with_context() {
329 let handler =
330 TypeHandler::new().add("test_ctx", |msg: TestMsg, ctx: MessageContext| async move {
331 assert_eq!(msg.val, "hello");
332 assert_eq!(ctx.metadata.get("meta").map(|s| s.as_str()), Some("data"));
333 Ok(Handled::Ack)
334 });
335
336 let msg = CanonicalMessage::from_type(&TestMsg {
337 val: "hello".into(),
338 })
339 .unwrap()
340 .with_metadata(HashMap::from([
341 ("kind".to_string(), "test_ctx".to_string()),
342 ("meta".to_string(), "data".to_string()),
343 ]));
344
345 let res = handler.handle(msg).await;
346 assert!(res.is_ok());
347 }
348
349 #[tokio::test]
350 async fn test_typed_handler_no_match_error() {
351 let handler = TypeHandler::new();
352 let msg = msg!(b"{}".to_vec(), "unknown");
353
354 let res = handler.handle(msg).await;
355 assert!(res.is_err());
356 match res.unwrap_err() {
357 HandlerError::NonRetryable(e) => {
358 assert!(e.to_string().contains("No handler registered"))
359 }
360 _ => panic!("Expected NonRetryable error"),
361 }
362 }
363
364 #[tokio::test]
365 async fn test_typed_handler_fallback_ack() {
366 let fallback = Arc::new(|_: CanonicalMessage| async { Ok(Handled::Ack) });
367 let handler = TypeHandler::new().with_fallback(fallback);
368
369 let msg = msg!(b"{}".to_vec(), "unknown");
370
371 let res = handler.handle(msg).await;
372 assert!(matches!(res, Ok(Handled::Ack)));
373 }
374
375 #[tokio::test]
376 async fn test_typed_handler_failure() {
377 let handler = TypeHandler::new().add("fail", |_: TestMsg| async {
378 Result::<Handled, HandlerError>::Err(HandlerError::Retryable(anyhow::anyhow!(
379 "failure"
380 )))
381 });
382
383 let msg = CanonicalMessage::from_type(&TestMsg { val: "x".into() })
384 .unwrap()
385 .with_type_key("fail");
386
387 let res = handler.handle(msg).await;
388 assert!(matches!(res, Err(HandlerError::Retryable(_))));
389 }
390
391 #[tokio::test]
392 async fn test_typed_handler_missing_type_key() {
393 let handler = TypeHandler::new().add("test", |_: TestMsg| async { Ok(Handled::Ack) });
394
395 let msg = CanonicalMessage::new(b"{}".to_vec(), None);
397
398 let res = handler.handle(msg).await;
399 assert!(res.is_err());
400 }
401
402 #[tokio::test]
403 async fn test_typed_handler_deserialization_failure() {
404 let handler = TypeHandler::new().add("test", |_: TestMsg| async { Ok(Handled::Ack) });
405
406 let msg = CanonicalMessage::new(b"{}".to_vec(), None)
408 .with_metadata(HashMap::from([("kind".to_string(), "test".to_string())]));
409
410 let res = handler.handle(msg).await;
411 assert!(matches!(res, Err(HandlerError::NonRetryable(_))));
412 }
413
414 #[tokio::test]
415 async fn test_typed_handler_handle_many_preserves_dispatch_order() {
416 struct RecordingHandler {
417 name: &'static str,
418 calls: Arc<Mutex<Vec<String>>>,
419 }
420
421 #[async_trait]
422 impl Handler for RecordingHandler {
423 async fn handle(&self, msg: CanonicalMessage) -> Result<Handled, HandlerError> {
424 self.calls
425 .lock()
426 .unwrap()
427 .push(format!("{}:{}", self.name, msg.get_payload_str()));
428 Ok(Handled::Ack)
429 }
430
431 async fn handle_many(
432 &self,
433 msgs: Vec<CanonicalMessage>,
434 ) -> Vec<Result<Handled, HandlerError>> {
435 let payloads = msgs
436 .iter()
437 .map(|msg| msg.get_payload_str())
438 .collect::<Vec<_>>()
439 .join(",");
440 self.calls
441 .lock()
442 .unwrap()
443 .push(format!("{}:{}", self.name, payloads));
444 msgs.into_iter().map(|_| Ok(Handled::Ack)).collect()
445 }
446 }
447
448 fn typed_msg(payload: &str, kind: &str) -> CanonicalMessage {
449 CanonicalMessage::from(payload)
450 .with_metadata(HashMap::from([("kind".to_string(), kind.to_string())]))
451 }
452
453 let calls = Arc::new(Mutex::new(Vec::new()));
454 let handler = TypeHandler::new()
455 .add_handler(
456 "a",
457 RecordingHandler {
458 name: "a",
459 calls: calls.clone(),
460 },
461 )
462 .add_handler(
463 "b",
464 RecordingHandler {
465 name: "b",
466 calls: calls.clone(),
467 },
468 );
469
470 let results = handler
471 .handle_many(vec![
472 typed_msg("a1", "a"),
473 typed_msg("a2", "a"),
474 typed_msg("b1", "b"),
475 typed_msg("a3", "a"),
476 ])
477 .await;
478
479 assert!(results.into_iter().all(|result| result.is_ok()));
480 assert_eq!(
481 *calls.lock().unwrap(),
482 vec![
483 "a:a1,a2".to_string(),
484 "b:b1".to_string(),
485 "a:a3".to_string()
486 ]
487 );
488 }
489
490 #[tokio::test]
491 async fn test_cqrs_pattern_example() {
492 #[derive(Serialize, Deserialize)]
493 struct SubmitOrder {
494 id: u32,
495 }
496
497 #[derive(Serialize, Deserialize)]
498 struct OrderSubmitted {
499 id: u32,
500 }
501
502 let command_bus = TypeHandler::new().add("submit_order", |cmd: SubmitOrder| async move {
504 let evt = OrderSubmitted { id: cmd.id };
507 Ok(Handled::Publish(msg!(&evt, "order_submitted")))
508 });
509
510 let projection_handler =
512 TypeHandler::new().add("order_submitted", |evt: OrderSubmitted| async move {
513 assert_eq!(evt.id, 101);
515 Ok(Handled::Ack)
516 });
517
518 let cmd = SubmitOrder { id: 101 };
520 let cmd_msg = msg!(&cmd, "submit_order");
521
522 let result = command_bus.handle(cmd_msg).await.unwrap();
524
525 if let Handled::Publish(event_msg) = result {
526 assert_eq!(
528 event_msg.metadata.get("kind").map(|s| s.as_str()),
529 Some("order_submitted")
530 );
531
532 let proj_result = projection_handler.handle(event_msg).await.unwrap();
534 assert!(matches!(proj_result, Handled::Ack));
535 } else {
536 panic!("Expected Handled::Publish");
537 }
538 }
539
540 #[tokio::test]
541 async fn test_cqrs_integration_with_routes() {
542 use crate::models::{Endpoint, Route};
543 use std::sync::atomic::{AtomicU32, Ordering};
544
545 #[derive(Serialize, Deserialize)]
546 struct SubmitOrder {
547 id: u32,
548 }
549
550 #[derive(Serialize, Deserialize)]
551 struct OrderSubmitted {
552 id: u32,
553 }
554
555 let read_model_state = Arc::new(AtomicU32::new(0));
557 let read_model_clone = read_model_state.clone();
558
559 let command_handler =
561 TypeHandler::new().add("submit_order", |cmd: SubmitOrder| async move {
562 let evt = OrderSubmitted { id: cmd.id };
563 Ok(Handled::Publish(msg!(&evt, "order_submitted")))
564 });
565
566 let event_handler =
568 TypeHandler::new().add("order_submitted", move |evt: OrderSubmitted| {
569 let state = read_model_clone.clone();
570 async move {
571 state.store(evt.id, Ordering::SeqCst);
572 Ok(Handled::Ack)
573 }
574 });
575
576 let cmd_in_ep = Endpoint::new_memory("cmd_in", 10);
578 let event_bus_ep = Endpoint::new_memory("event_bus", 10);
579 let proj_out_ep = Endpoint::new_memory("proj_out", 10);
580
581 let command_route =
582 Route::new(cmd_in_ep.clone(), event_bus_ep.clone()).with_handler(command_handler);
583
584 let event_route =
585 Route::new(event_bus_ep.clone(), proj_out_ep.clone()).with_handler(event_handler);
586
587 let h1 = tokio::spawn(async move {
589 command_route
590 .run_until_err("command_route", None, None)
591 .await
592 });
593 let h2 =
594 tokio::spawn(async move { event_route.run_until_err("event_route", None, None).await });
595
596 let cmd_channel = cmd_in_ep.channel().unwrap();
598 let cmd = SubmitOrder { id: 777 };
599 let msg = CanonicalMessage::from_type(&cmd)
600 .unwrap()
601 .with_type_key("submit_order");
602 cmd_channel.send_message(msg).await.unwrap();
603
604 let mut attempts = 0;
606 while read_model_state.load(Ordering::SeqCst) != 777 && attempts < 50 {
607 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
608 attempts += 1;
609 }
610
611 assert_eq!(read_model_state.load(Ordering::SeqCst), 777);
612
613 cmd_channel.close();
615 event_bus_ep.channel().unwrap().close();
616
617 let _ = h1.await;
618 let _ = h2.await;
619 }
620}