1use crate::traits::{BoxFuture, Handler, MessagePublisher};
7use crate::traits::{Handled, HandlerError};
8use crate::CanonicalMessage;
9use async_trait::async_trait;
10use std::any::Any;
11use std::future::Future;
12use std::sync::Arc;
13
14use crate::traits::{PublisherError, Sent, SentBatch};
15#[async_trait]
16impl<F, Fut> Handler for F
17where
18 F: Fn(CanonicalMessage) -> Fut + Send + Sync + 'static,
19 Fut: Future<Output = Result<Handled, HandlerError>> + Send,
20{
21 async fn handle(&self, msg: CanonicalMessage) -> Result<Handled, HandlerError> {
22 self(msg).await
23 }
24}
25
26pub struct CommandPublisher {
29 inner: Box<dyn MessagePublisher>,
30 handler: Arc<dyn Handler>,
31}
32
33impl CommandPublisher {
34 pub fn new(inner: impl MessagePublisher, handler: impl Handler + 'static) -> Self {
35 Self {
36 inner: Box::new(inner),
37 handler: Arc::new(handler),
38 }
39 }
40}
41
42#[async_trait]
43impl MessagePublisher for CommandPublisher {
44 fn on_connect_hook(&self) -> Option<BoxFuture<'_, anyhow::Result<()>>> {
45 self.inner.on_connect_hook()
46 }
47
48 fn on_disconnect_hook(&self) -> Option<BoxFuture<'_, anyhow::Result<()>>> {
49 self.inner.on_disconnect_hook()
50 }
51
52 async fn send(&self, message: CanonicalMessage) -> Result<Sent, PublisherError> {
53 let inbound_correlation_id = message.metadata.get("correlation_id").cloned();
54 let original_id = message.message_id;
55 match self.handler.handle(message).await {
56 Ok(Handled::Publish(mut response_msg)) => {
57 response_msg.message_id = original_id;
59 let fallback_correlation_id =
61 inbound_correlation_id.unwrap_or_else(|| format!("{:032x}", original_id));
62 response_msg
63 .metadata
64 .entry("correlation_id".to_string())
65 .or_insert(fallback_correlation_id);
66 self.inner.send(response_msg).await
67 }
68 Ok(Handled::Ack) => Ok(Sent::Ack),
69 Err(e) => Err(e), }
71 }
72
73 async fn send_batch(
74 &self,
75 messages: Vec<CanonicalMessage>,
76 ) -> Result<SentBatch, PublisherError> {
77 let handler_results = self.handler.handle_many(messages.clone()).await;
78
79 if handler_results.len() != messages.len() {
80 return Err(PublisherError::NonRetryable(anyhow::anyhow!(
81 "handler returned {} results for {} messages",
82 handler_results.len(),
83 messages.len()
84 )));
85 }
86
87 let mut to_publish = Vec::with_capacity(messages.len());
92 let mut failed: Vec<(CanonicalMessage, PublisherError)> = Vec::new();
93
94 for (message, result) in messages.into_iter().zip(handler_results) {
95 let original_id = message.message_id;
96 match result {
97 Ok(Handled::Ack) => {}
98 Ok(Handled::Publish(mut response_msg)) => {
99 let inbound_correlation_id = message.metadata.get("correlation_id").cloned();
100 response_msg.message_id = original_id;
101 let fallback_correlation_id =
102 inbound_correlation_id.unwrap_or_else(|| format!("{:032x}", original_id));
103 response_msg
104 .metadata
105 .entry("correlation_id".to_string())
106 .or_insert(fallback_correlation_id);
107 to_publish.push(response_msg);
108 }
109 Err(HandlerError::NonRetryable(err)) => {
110 failed.push((message, PublisherError::NonRetryable(err)));
111 }
112 Err(HandlerError::Retryable(err)) => {
113 failed.push((message, PublisherError::Retryable(err)));
114 }
115 Err(HandlerError::Connection(err)) => {
116 failed.push((message, PublisherError::Connection(err)));
117 }
118 }
119 }
120
121 if to_publish.is_empty() {
122 return if failed.is_empty() {
123 Ok(SentBatch::Ack)
124 } else {
125 Ok(SentBatch::Partial {
126 responses: None,
127 failed,
128 })
129 };
130 }
131
132 match self.inner.send_batch(to_publish).await {
133 Ok(SentBatch::Ack) => {
134 if failed.is_empty() {
135 Ok(SentBatch::Ack)
136 } else {
137 Ok(SentBatch::Partial {
138 responses: None,
139 failed,
140 })
141 }
142 }
143 Ok(SentBatch::Partial {
144 responses,
145 failed: inner_failed,
146 }) => {
147 failed.extend(inner_failed);
148 Ok(SentBatch::Partial { responses, failed })
149 }
150 Err(err) => Err(err),
153 }
154 }
155
156 async fn flush(&self) -> anyhow::Result<()> {
157 self.inner.flush().await
158 }
159
160 fn as_any(&self) -> &dyn Any {
161 self
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
168
169 use super::*;
170 use crate::endpoints::memory::MemoryPublisher;
171
172 #[tokio::test]
173 async fn test_command_handler_produces_response() {
174 let memory_publisher = MemoryPublisher::new_local("test_command_out_resp", 10);
175 let channel = memory_publisher.channel();
176
177 let handler = |msg: CanonicalMessage| async move {
178 let response_payload = format!("response_to_{}", String::from_utf8_lossy(&msg.payload));
179 Ok(Handled::Publish(response_payload.into()))
180 };
181
182 let publisher = CommandPublisher::new(memory_publisher, handler);
183
184 publisher.send("command1".into()).await.unwrap();
185
186 let received = channel.drain_messages();
187 assert_eq!(received.len(), 1);
188 assert_eq!(received[0].payload, "response_to_command1".as_bytes());
189 }
190
191 #[tokio::test]
192 async fn test_command_handler_acks() {
193 let memory_publisher = MemoryPublisher::new_local("test_command_out_ack", 10);
194 let channel = memory_publisher.channel();
195
196 let handler = |_msg: CanonicalMessage| async move { Ok(Handled::Ack) };
197
198 let publisher = CommandPublisher::new(memory_publisher, handler);
199
200 let result = publisher.send("command1".into()).await.unwrap();
201
202 assert!(matches!(result, Sent::Ack));
203 let received = channel.drain_messages();
204 assert_eq!(received.len(), 0);
205 }
206
207 #[tokio::test]
208 async fn test_command_handler_retryable_error() {
209 let memory_publisher = MemoryPublisher::new_local("test_command_out_err", 10);
210
211 let handler = |_msg: CanonicalMessage| async move {
212 Err(HandlerError::Retryable(anyhow::anyhow!("db is down")))
213 };
214
215 let publisher = CommandPublisher::new(memory_publisher, handler);
216 let result = publisher.send("command1".into()).await;
217
218 assert!(result.is_err());
219 let err = result.unwrap_err();
220 assert!(matches!(err, PublisherError::Retryable(_)));
222 }
223
224 #[tokio::test]
225 async fn test_command_handler_integration_with_memory_consumer() {
226 use crate::endpoints::memory::MemoryConsumer;
227 use crate::traits::MessageConsumer;
228
229 let mut consumer = MemoryConsumer::new_local("cmd_input", 10);
230 let input_channel = consumer.channel();
231
232 let memory_publisher = MemoryPublisher::new_local("cmd_output", 10);
234 let output_channel = memory_publisher.channel();
235
236 let publisher =
238 CommandPublisher::new(memory_publisher, |msg: CanonicalMessage| async move {
239 let payload = String::from_utf8_lossy(&msg.payload);
240 let response = format!("processed_{}", payload);
241 Ok(Handled::Publish(response.into()))
242 });
243
244 input_channel
245 .send_message("test_data".into())
246 .await
247 .unwrap();
248
249 let received = consumer.receive().await.unwrap();
251 let result = publisher.send(received.message).await.unwrap();
252
253 assert!(matches!(result, Sent::Ack));
254
255 let output_msgs = output_channel.drain_messages();
256 assert_eq!(output_msgs.len(), 1);
257 assert_eq!(output_msgs[0].payload.to_vec(), b"processed_test_data");
258
259 let _ = (received.commit)(crate::traits::MessageDisposition::Ack).await;
260 }
261
262 #[tokio::test(flavor = "multi_thread")]
263 async fn test_command_handler_with_route_config() {
264 use crate::models::{Endpoint, Route};
265
266 let success = Arc::new(AtomicBool::new(false));
267 let success_clone = success.clone();
268
269 let handler = move |mut msg: CanonicalMessage| {
270 success_clone.store(true, Ordering::SeqCst);
271 msg.set_payload_str(format!("modified {}", msg.get_payload_str()));
272 async move { Ok(Handled::Publish(msg)) }
273 };
274 let route = Route::new(
275 Endpoint::new_memory("route_in", 100),
276 Endpoint::new_memory("route_out", 100),
277 )
278 .with_handler(handler);
279
280 route.deploy("command_handler_test_route").await.unwrap();
281
282 let input_channel = route.input.channel().unwrap();
283 input_channel.send_message("hello".into()).await.unwrap();
284
285 let mut verifier = route.connect_to_output("verifier").await.unwrap();
286 let received = verifier.receive().await.unwrap();
287 assert_eq!(received.message.get_payload_str(), "modified hello");
288 assert!(success.load(Ordering::SeqCst));
289 Route::stop("command_handler_test_route").await;
290 }
291
292 #[tokio::test]
293 async fn test_command_handler_inner_publisher_failure() {
294 use crate::traits::MessagePublisher;
295
296 struct FailPublisher;
297 #[async_trait]
298 impl MessagePublisher for FailPublisher {
299 async fn send(&self, _msg: CanonicalMessage) -> Result<Sent, PublisherError> {
300 Err(PublisherError::NonRetryable(anyhow::anyhow!("inner fail")))
301 }
302 async fn send_batch(
303 &self,
304 _msgs: Vec<CanonicalMessage>,
305 ) -> Result<SentBatch, PublisherError> {
306 Ok(SentBatch::Ack)
307 }
308 fn as_any(&self) -> &dyn std::any::Any {
309 self
310 }
311 }
312
313 let handler = |msg: CanonicalMessage| async move { Ok(Handled::Publish(msg)) };
314 let publisher = CommandPublisher::new(FailPublisher, handler);
315 let result = publisher.send("test".into()).await;
316 assert!(result.is_err());
317 assert!(result.unwrap_err().to_string().contains("inner fail"));
318 }
319
320 #[tokio::test]
321 async fn test_command_handler_preserves_message_id() {
322 let memory_publisher = MemoryPublisher::new_local("test_cmd_id_preservation", 10);
323 let channel = memory_publisher.channel();
324
325 let handler = |_msg: CanonicalMessage| async move {
326 let new_msg = CanonicalMessage::new(b"response".to_vec(), None);
327 Ok(Handled::Publish(new_msg))
328 };
329
330 let publisher = CommandPublisher::new(memory_publisher, handler);
331 let original_id = 987654321u128;
332 publisher
333 .send(CanonicalMessage::new(b"req".to_vec(), Some(original_id)))
334 .await
335 .unwrap();
336
337 let received = channel.drain_messages();
338 assert_eq!(received[0].message_id, original_id);
339 }
340
341 #[tokio::test]
342 async fn test_command_handler_send_batch_uses_handler_handle_many() {
343 struct BatchAwareHandler {
344 single_calls: AtomicUsize,
345 batch_calls: AtomicUsize,
346 }
347
348 #[async_trait]
349 impl Handler for BatchAwareHandler {
350 async fn handle(&self, _msg: CanonicalMessage) -> Result<Handled, HandlerError> {
351 self.single_calls.fetch_add(1, Ordering::SeqCst);
352 Ok(Handled::Ack)
353 }
354
355 async fn handle_many(
356 &self,
357 msgs: Vec<CanonicalMessage>,
358 ) -> Vec<Result<Handled, HandlerError>> {
359 self.batch_calls.fetch_add(1, Ordering::SeqCst);
360 msgs.into_iter()
361 .map(|mut msg| {
362 msg.set_payload_str(format!("batched {}", msg.get_payload_str()));
363 Ok(Handled::Publish(msg))
364 })
365 .collect()
366 }
367 }
368
369 let memory_publisher = MemoryPublisher::new_local("test_cmd_batch_many", 10);
370 let channel = memory_publisher.channel();
371 let handler = Arc::new(BatchAwareHandler {
372 single_calls: AtomicUsize::new(0),
373 batch_calls: AtomicUsize::new(0),
374 });
375 let publisher = CommandPublisher::new(memory_publisher, handler.clone());
376
377 let result = publisher
378 .send_batch(vec!["one".into(), "two".into(), "three".into()])
379 .await
380 .unwrap();
381
382 assert!(matches!(result, SentBatch::Ack));
383 assert_eq!(handler.single_calls.load(Ordering::SeqCst), 0);
384 assert_eq!(handler.batch_calls.load(Ordering::SeqCst), 1);
385
386 let received = channel.drain_messages();
387 assert_eq!(received.len(), 3);
388 assert_eq!(received[0].get_payload_str(), "batched one");
389 assert_eq!(received[1].get_payload_str(), "batched two");
390 assert_eq!(received[2].get_payload_str(), "batched three");
391 }
392
393 #[tokio::test]
394 async fn test_command_handler_send_batch_non_retryable_handler_error_continues() {
395 struct PartiallyFailingHandler;
396
397 #[async_trait]
398 impl Handler for PartiallyFailingHandler {
399 async fn handle(&self, _msg: CanonicalMessage) -> Result<Handled, HandlerError> {
400 unreachable!("send_batch should use handle_many")
401 }
402
403 async fn handle_many(
404 &self,
405 msgs: Vec<CanonicalMessage>,
406 ) -> Vec<Result<Handled, HandlerError>> {
407 msgs.into_iter()
408 .map(|msg| {
409 if msg.get_payload_str() == "two" {
410 Err(HandlerError::NonRetryable(anyhow::anyhow!("bad message")))
411 } else {
412 Ok(Handled::Publish(msg))
413 }
414 })
415 .collect()
416 }
417 }
418
419 let memory_publisher = MemoryPublisher::new_local("test_cmd_batch_non_retryable", 10);
420 let channel = memory_publisher.channel();
421 let publisher = CommandPublisher::new(memory_publisher, PartiallyFailingHandler);
422
423 let result = publisher
424 .send_batch(vec!["one".into(), "two".into(), "three".into()])
425 .await
426 .unwrap();
427
428 match result {
429 SentBatch::Partial { responses, failed } => {
430 assert!(responses.is_none());
431 assert_eq!(failed.len(), 1);
432 assert_eq!(failed[0].0.get_payload_str(), "two");
433 assert!(matches!(failed[0].1, PublisherError::NonRetryable(_)));
434 }
435 other => panic!("expected partial failure, got {other:?}"),
436 }
437
438 let received = channel.drain_messages();
439 assert_eq!(received.len(), 2);
440 assert_eq!(received[0].get_payload_str(), "one");
441 assert_eq!(received[1].get_payload_str(), "three");
442 }
443
444 #[tokio::test]
445 async fn test_command_handler_send_batch_batches_inner_publish_and_reports_per_message_failures(
446 ) {
447 struct PublishAllHandler;
448
449 #[async_trait]
450 impl Handler for PublishAllHandler {
451 async fn handle(&self, _msg: CanonicalMessage) -> Result<Handled, HandlerError> {
452 unreachable!("send_batch should use handle_many")
453 }
454
455 async fn handle_many(
456 &self,
457 msgs: Vec<CanonicalMessage>,
458 ) -> Vec<Result<Handled, HandlerError>> {
459 msgs.into_iter()
460 .map(|msg| Ok(Handled::Publish(msg)))
461 .collect()
462 }
463 }
464
465 struct RetryableSecondMessagePublisher {
468 batch_calls: Arc<AtomicUsize>,
469 }
470
471 #[async_trait]
472 impl MessagePublisher for RetryableSecondMessagePublisher {
473 async fn send(&self, _msg: CanonicalMessage) -> Result<Sent, PublisherError> {
474 unreachable!("command batch publishing should use a single inner send_batch call")
475 }
476
477 async fn send_batch(
478 &self,
479 msgs: Vec<CanonicalMessage>,
480 ) -> Result<SentBatch, PublisherError> {
481 self.batch_calls.fetch_add(1, Ordering::SeqCst);
482 let failed = msgs
483 .into_iter()
484 .filter(|msg| msg.get_payload_str() == "two")
485 .map(|msg| {
486 (
487 msg,
488 PublisherError::Retryable(anyhow::anyhow!("temporary failure")),
489 )
490 })
491 .collect();
492 Ok(SentBatch::Partial {
493 responses: None,
494 failed,
495 })
496 }
497
498 fn as_any(&self) -> &dyn std::any::Any {
499 self
500 }
501 }
502
503 let batch_calls = Arc::new(AtomicUsize::new(0));
504 let publisher = CommandPublisher::new(
505 RetryableSecondMessagePublisher {
506 batch_calls: batch_calls.clone(),
507 },
508 PublishAllHandler,
509 );
510
511 let result = publisher
512 .send_batch(vec!["one".into(), "two".into(), "three".into()])
513 .await
514 .unwrap();
515
516 assert_eq!(
517 batch_calls.load(Ordering::SeqCst),
518 1,
519 "the whole batch should be sent in a single inner send_batch call"
520 );
521 match result {
522 SentBatch::Partial { responses, failed } => {
523 assert!(responses.is_none());
524 assert_eq!(failed.len(), 1);
525 assert_eq!(failed[0].0.get_payload_str(), "two");
526 assert!(matches!(failed[0].1, PublisherError::Retryable(_)));
527 }
528 other => panic!("expected partial failure, got {other:?}"),
529 }
530 }
531}