1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Content-Based Router pattern: route an `Exchange` to one of several processors based on a header value.
//!
//! This implements the classic Enterprise Integration Pattern (EIP) "Content-Based Router".
//! It examines a specific message header and selects a processor whose configured value matches.
//!
//! # Why It Exists
//! In message-driven integrations you often need to send different message types (or business events)
//! through distinct processing flows. Instead of embedding conditional logic inside processors or
//! scattering `if/else` routes, a dedicated router centralizes this decision, improving readability
//! and testability.
//!
//! # Why In `patterns` Folder
//! The `patterns` module groups canonical EIP building blocks (filter, splitter, aggregator, etc.).
//! `ContentBasedRouter` is one of those core patterns, so it lives alongside them for discoverability
//! and conceptual cohesion rather than in a generic utilities namespace.
//!
//! # Behavior
//! * Looks up a header (configured via `new(header_name)`).
//! * If the header matches a registered route value (added via `when(value, processor)`), the associated
//! processor is invoked.
//! * If no route matches, returns a `Routing` error (`Error::Routing`).
//! * Exactly one processor is invoked (first matching key lookup).
//!
//! # Async vs Sync
//! The router itself is lightweight; it defers to the selected processor. Under the `async` feature
//! its `process` method is async and awaits the downstream processor; without it, synchronous dispatch occurs.
//!
//! # Example (basic usage)
//! ```rust
//! use allora_core::{patterns::content_router::ContentBasedRouter, processor::ClosureProcessor, route::Route, Exchange, Message};
//! let hi = ClosureProcessor::new(|exchange| { exchange.out_msg = Some(Message::from_text("hi route")); Ok(()) });
//! let bye = ClosureProcessor::new(|exchange| { exchange.out_msg = Some(Message::from_text("bye route")); Ok(()) });
//! let router = ContentBasedRouter::new("kind").when("hi", Box::new(hi)).when("bye", Box::new(bye));
//! let route = Route::new().add(router).build();
//! let mut exchange = Exchange::new(Message::from_text("payload"));
//! exchange.in_msg.set_header("kind", "hi");
//! let rt = tokio::runtime::Runtime::new().unwrap();
//! rt.block_on(async { route.run(&mut exchange).await.unwrap(); });
//! assert_eq!(exchange.out_msg.unwrap().body_text(), Some("hi route"));
//! ```
//!
//! # Error Handling
//! No match: `Error::Routing("no matching route")`.
//! Downstream processor errors propagate unchanged.
//!
//! # Testing Strategies
//! * Provide several `when` routes, assert correct one chosen.
//! * Assert error when header missing.
//! * Use processors that mutate headers to verify only one is executed.
//!
//! # Future Extensions
//! * Predicate-based routing (closures) instead of plain equality.
//! * Default / fallback processor.
//! * Support for wildcards / pattern matching.
//! * Metrics counters (per route hit counts).
use crate::;
use HashMap;
use Debug;