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