1use std::collections::{HashSet, VecDeque};
4use std::sync::Arc;
5use std::time::Duration;
6
7use async_trait::async_trait;
8use rskit_errors::{AppError, AppResult, ErrorCode};
9use tokio::sync::{Mutex, broadcast};
10
11use crate::config::BrokerConfig;
12use crate::event::Event;
13use crate::message::Message;
14use crate::registry::{MessagingFactory, MessagingRegistry};
15use crate::traits::{EventConsumer, EventProducer, MessageConsumer, MessageProducer};
16
17const ADAPTER_NAME: &str = "memory";
18
19#[derive(Debug, Clone)]
27pub struct InMemoryBroker<T: Clone + Send + Sync + 'static> {
28 tx: broadcast::Sender<Message<T>>,
29 history: Arc<Mutex<VecDeque<Message<T>>>>,
30 history_limit: Option<usize>,
31 topics: Arc<Mutex<HashSet<String>>>,
32 notify: Arc<tokio::sync::Notify>,
35}
36
37impl<T: Clone + Send + Sync + 'static> InMemoryBroker<T> {
38 pub fn new(capacity: usize) -> Self {
40 let limit = capacity.max(1);
41 let (tx, _) = broadcast::channel(limit);
42 Self {
43 tx,
44 history: Arc::new(Mutex::new(VecDeque::with_capacity(limit))),
45 history_limit: Some(limit),
46 topics: Arc::new(Mutex::new(HashSet::new())),
47 notify: Arc::new(tokio::sync::Notify::new()),
48 }
49 }
50
51 pub fn producer(&self) -> InMemoryProducer<T> {
53 InMemoryProducer {
54 tx: self.tx.clone(),
55 history: self.history.clone(),
56 history_limit: self.history_limit,
57 topics: self.topics.clone(),
58 notify: self.notify.clone(),
59 }
60 }
61
62 #[must_use]
67 pub fn with_history_limit(capacity: usize, history_limit: usize) -> Self {
68 let capacity = capacity.max(1);
69 let limit = history_limit.max(1);
70 let (tx, _) = broadcast::channel(capacity);
71 Self {
72 tx,
73 history: Arc::new(Mutex::new(VecDeque::with_capacity(limit))),
74 history_limit: Some(limit),
75 topics: Arc::new(Mutex::new(HashSet::new())),
76 notify: Arc::new(tokio::sync::Notify::new()),
77 }
78 }
79
80 pub fn consumer(&self) -> InMemoryConsumer<T> {
82 InMemoryConsumer {
83 rx: Arc::new(Mutex::new(self.tx.subscribe())),
84 topics: Arc::new(Mutex::new(HashSet::new())),
85 }
86 }
87
88 pub async fn messages(&self, topic: &str) -> Vec<Message<T>> {
90 self.history
91 .lock()
92 .await
93 .iter()
94 .filter(|m| m.topic == topic)
95 .cloned()
96 .collect()
97 }
98
99 pub async fn all_messages(&self) -> Vec<Message<T>> {
101 self.history.lock().await.iter().cloned().collect()
102 }
103
104 pub async fn message_count(&self, topic: &str) -> usize {
106 self.history
107 .lock()
108 .await
109 .iter()
110 .filter(|m| m.topic == topic)
111 .count()
112 }
113
114 pub async fn reset(&self) {
116 self.history.lock().await.clear();
117 }
118
119 pub async fn create_topic(&self, topic: &str) {
121 self.topics.lock().await.insert(topic.to_string());
122 }
123
124 pub async fn topic_names(&self) -> Vec<String> {
126 let mut set: HashSet<String> = self.topics.lock().await.clone();
127 {
128 let hist = self.history.lock().await;
129 for m in hist.iter() {
130 set.insert(m.topic.clone());
131 }
132 }
133 let mut out: Vec<String> = set.into_iter().collect();
134 out.sort();
135 out
136 }
137}
138
139impl<T: Clone + Send + Sync + 'static> Default for InMemoryBroker<T> {
140 fn default() -> Self {
141 Self::new(256)
142 }
143}
144
145pub fn register<T: Clone + Send + Sync + 'static>(
147 registry: &mut MessagingRegistry<T>,
148 broker: InMemoryBroker<T>,
149) -> AppResult<()> {
150 registry.register_backend(ADAPTER_NAME, Arc::new(MemoryFactory { broker }))
151}
152
153struct MemoryFactory<T: Clone + Send + Sync + 'static> {
154 broker: InMemoryBroker<T>,
155}
156
157impl<T: Clone + Send + Sync + 'static> MessagingFactory<T> for MemoryFactory<T> {
158 fn create_producer(&self, _config: &BrokerConfig) -> AppResult<Arc<dyn MessageProducer<T>>> {
159 Ok(Arc::new(self.broker.producer()))
160 }
161
162 fn create_consumer(&self, _config: &BrokerConfig) -> AppResult<Arc<dyn MessageConsumer<T>>> {
163 Ok(Arc::new(self.broker.consumer()))
164 }
165}
166
167#[derive(Debug, Clone)]
169pub struct InMemoryProducer<T: Clone + Send + Sync + 'static> {
170 tx: broadcast::Sender<Message<T>>,
171 history: Arc<Mutex<VecDeque<Message<T>>>>,
172 history_limit: Option<usize>,
173 topics: Arc<Mutex<HashSet<String>>>,
174 notify: Arc<tokio::sync::Notify>,
175}
176
177#[async_trait]
178impl<T: Clone + Send + Sync + 'static> MessageProducer<T> for InMemoryProducer<T> {
179 async fn send(&self, msg: Message<T>) -> AppResult<()> {
180 {
182 let mut hist = self.history.lock().await;
183 if let Some(limit) = self.history_limit
184 && hist.len() == limit
185 {
186 hist.pop_front();
187 }
188 hist.push_back(msg.clone());
189 }
190 {
191 let mut set = self.topics.lock().await;
192 set.insert(msg.topic.clone());
193 }
194
195 self.tx.send(msg).map_err(|_| {
196 AppError::new(ErrorCode::ExternalService, "no active consumers on channel")
197 })?;
198
199 self.notify.notify_waiters();
200 Ok(())
201 }
202
203 async fn send_batch(&self, msgs: Vec<Message<T>>) -> AppResult<()> {
204 for msg in msgs {
205 self.send(msg).await?;
206 }
207 Ok(())
208 }
209
210 async fn flush(&self, _timeout: Duration) -> AppResult<()> {
211 Ok(())
213 }
214}
215
216#[derive(Debug)]
218pub struct InMemoryConsumer<T: Clone + Send + Sync + 'static> {
219 rx: Arc<Mutex<broadcast::Receiver<Message<T>>>>,
220 topics: Arc<Mutex<HashSet<String>>>,
221}
222
223impl<T: Clone + Send + Sync + 'static> Clone for InMemoryConsumer<T> {
225 fn clone(&self) -> Self {
226 Self {
227 rx: self.rx.clone(),
228 topics: self.topics.clone(),
229 }
230 }
231}
232
233#[async_trait]
234impl<T: Clone + Send + Sync + 'static> MessageConsumer<T> for InMemoryConsumer<T> {
235 async fn subscribe(&self, topics: &[&str]) -> AppResult<()> {
236 {
237 let mut set = self.topics.lock().await;
238 for t in topics {
239 set.insert((*t).to_string());
240 }
241 }
242 Ok(())
243 }
244
245 async fn recv(&self, timeout: std::time::Duration) -> AppResult<Message<T>> {
246 if timeout.is_zero() {
247 return Err(AppError::new(
248 ErrorCode::InvalidInput,
249 "message receive timeout must be greater than zero",
250 ));
251 }
252 tokio::time::timeout(timeout, async {
253 loop {
254 let msg = {
255 let mut rx = self.rx.lock().await;
256 rx.recv().await.map_err(|e| {
257 AppError::new(ErrorCode::ExternalService, format!("receive failed: {e}"))
258 })?
259 };
260
261 let topics = self.topics.lock().await;
262 if topics.is_empty() || topics.contains(&msg.topic) {
264 return Ok(msg);
265 }
266 }
268 })
269 .await
270 .map_err(|error| AppError::timeout("message receive").with_cause(error))?
271 }
272}
273
274#[async_trait]
275impl EventProducer for InMemoryProducer<serde_json::Value> {
276 async fn publish(&self, topic: &str, event: Event) -> AppResult<()> {
277 let value = serde_json::to_value(&event).map_err(|e| {
278 AppError::new(
279 ErrorCode::Internal,
280 format!("Failed to serialize event: {e}"),
281 )
282 })?;
283 self.send(Message::new(topic, value)).await
284 }
285
286 async fn publish_batch(&self, topic: &str, events: Vec<Event>) -> AppResult<()> {
287 for event in events {
288 self.publish(topic, event).await?;
289 }
290 Ok(())
291 }
292}
293
294#[async_trait]
295impl EventConsumer for InMemoryConsumer<serde_json::Value> {
296 async fn subscribe(&self, topics: &[&str]) -> AppResult<()> {
297 MessageConsumer::subscribe(self, topics).await
298 }
299
300 async fn recv_event(&self, timeout: std::time::Duration) -> AppResult<Event> {
301 let msg = self.recv(timeout).await?;
302 serde_json::from_value(msg.payload).map_err(|e| {
303 AppError::new(
304 ErrorCode::Internal,
305 format!("Failed to deserialize event: {e}"),
306 )
307 })
308 }
309}
310
311pub async fn assert_published<T: Clone + Send + Sync + 'static>(
317 broker: &InMemoryBroker<T>,
318 topic: &str,
319 predicate: impl Fn(&Message<T>) -> bool,
320) {
321 let msgs = broker.messages(topic).await;
322 assert!(
323 msgs.iter().any(&predicate),
324 "assert_published: no message on topic {topic:?} matched the predicate ({} checked)",
325 msgs.len(),
326 );
327}
328
329pub async fn assert_published_n<T: Clone + Send + Sync + 'static>(
335 broker: &InMemoryBroker<T>,
336 topic: &str,
337 n: usize,
338) {
339 let got = broker.message_count(topic).await;
340 assert_eq!(
341 got, n,
342 "assert_published_n: topic {topic:?} has {got} messages, want {n}",
343 );
344}
345
346pub async fn wait_for_message<T: Clone + Send + Sync + 'static>(
354 broker: &InMemoryBroker<T>,
355 topic: &str,
356 timeout: Duration,
357) -> Message<T> {
358 let deadline = tokio::time::Instant::now() + timeout;
359
360 loop {
361 let msgs = broker.messages(topic).await;
362 if let Some(m) = msgs.into_iter().next() {
363 return m;
364 }
365 tokio::select! {
366 () = broker.notify.notified() => { }
367 () = tokio::time::sleep_until(deadline) => {
368 panic!("wait_for_message: timed out after {timeout:?} waiting for message on topic {topic:?}");
369 }
370 }
371 }
372}
373
374pub async fn assert_no_messages<T: Clone + Send + Sync + 'static>(
380 broker: &InMemoryBroker<T>,
381 topic: &str,
382) {
383 let n = broker.message_count(topic).await;
384 assert_eq!(
385 n, 0,
386 "assert_no_messages: topic {topic:?} has {n} messages, want 0",
387 );
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[tokio::test]
395 async fn send_and_receive() {
396 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
397 let producer = broker.producer();
398 let consumer = broker.consumer();
399
400 consumer.subscribe(&["test-topic"]).await.unwrap();
401
402 let msg = Message::new("test-topic", "hello".to_string());
403 producer.send(msg).await.unwrap();
404
405 let received = consumer
406 .recv(std::time::Duration::from_secs(1))
407 .await
408 .unwrap();
409 assert_eq!(received.topic, "test-topic");
410 assert_eq!(received.payload, "hello");
411 }
412
413 #[tokio::test]
414 async fn register_memory_adapter_explicitly() {
415 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
416 let mut registry = MessagingRegistry::new();
417
418 register(&mut registry, broker).unwrap();
419
420 assert_eq!(registry.adapters(), vec!["memory"]);
421 let config = BrokerConfig::default();
422 let producer = registry.producer(&config).unwrap();
423 let consumer = registry.consumer(&config).unwrap();
424 consumer.subscribe(&["events"]).await.unwrap();
425 producer
426 .send(Message::new("events", "registered".to_string()))
427 .await
428 .unwrap();
429 let received = consumer
430 .recv(std::time::Duration::from_secs(1))
431 .await
432 .unwrap();
433 assert_eq!(received.payload, "registered");
434 }
435
436 #[tokio::test]
437 async fn send_batch_and_receive() {
438 let broker: InMemoryBroker<i32> = InMemoryBroker::new(16);
439 let producer = broker.producer();
440 let consumer = broker.consumer();
441
442 consumer.subscribe(&["numbers"]).await.unwrap();
443
444 let msgs = vec![
445 Message::new("numbers", 1),
446 Message::new("numbers", 2),
447 Message::new("numbers", 3),
448 ];
449 producer.send_batch(msgs).await.unwrap();
450
451 let a = consumer
452 .recv(std::time::Duration::from_secs(1))
453 .await
454 .unwrap();
455 let b = consumer
456 .recv(std::time::Duration::from_secs(1))
457 .await
458 .unwrap();
459 let c = consumer
460 .recv(std::time::Duration::from_secs(1))
461 .await
462 .unwrap();
463 assert_eq!(a.payload, 1);
464 assert_eq!(b.payload, 2);
465 assert_eq!(c.payload, 3);
466 }
467
468 #[tokio::test]
469 async fn topic_filtering() {
470 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
471 let producer = broker.producer();
472 let consumer = broker.consumer();
473
474 consumer.subscribe(&["wanted"]).await.unwrap();
475
476 producer
477 .send(Message::new("ignored", "nope".to_string()))
478 .await
479 .unwrap();
480 producer
481 .send(Message::new("wanted", "yes".to_string()))
482 .await
483 .unwrap();
484
485 let received = consumer
486 .recv(std::time::Duration::from_secs(1))
487 .await
488 .unwrap();
489 assert_eq!(received.topic, "wanted");
490 assert_eq!(received.payload, "yes");
491 }
492
493 #[tokio::test]
494 async fn flush_is_noop() {
495 let broker: InMemoryBroker<()> = InMemoryBroker::new(4);
496 let producer = broker.producer();
497 producer.flush(Duration::from_secs(1)).await.unwrap();
498 }
499
500 #[tokio::test]
501 async fn event_publish_and_receive() {
502 let broker: InMemoryBroker<serde_json::Value> = InMemoryBroker::new(16);
503 let producer = broker.producer();
504 let consumer = broker.consumer();
505
506 EventConsumer::subscribe(&consumer, &["events"])
507 .await
508 .unwrap();
509
510 let event = Event::new("user.created", "auth-service")
511 .with_subject("user-42")
512 .with_data(serde_json::json!({"name": "Alice"}))
513 .unwrap();
514 let original_id = event.id.clone();
515
516 producer.publish("events", event).await.unwrap();
517
518 let received = consumer
519 .recv_event(std::time::Duration::from_secs(1))
520 .await
521 .unwrap();
522 assert_eq!(received.id, original_id);
523 assert_eq!(received.event_type, "user.created");
524 assert_eq!(received.source, "auth-service");
525 assert_eq!(received.subject, "user-42");
526 assert_eq!(received.data, serde_json::json!({"name": "Alice"}));
527 }
528
529 #[tokio::test]
530 async fn event_publish_batch_and_receive() {
531 let broker: InMemoryBroker<serde_json::Value> = InMemoryBroker::new(16);
532 let producer = broker.producer();
533 let consumer = broker.consumer();
534
535 EventConsumer::subscribe(&consumer, &["batch"])
536 .await
537 .unwrap();
538
539 let events = vec![
540 Event::new("a", "src"),
541 Event::new("b", "src"),
542 Event::new("c", "src"),
543 ];
544 producer.publish_batch("batch", events).await.unwrap();
545
546 let a = consumer
547 .recv_event(std::time::Duration::from_secs(1))
548 .await
549 .unwrap();
550 let b = consumer
551 .recv_event(std::time::Duration::from_secs(1))
552 .await
553 .unwrap();
554 let c = consumer
555 .recv_event(std::time::Duration::from_secs(1))
556 .await
557 .unwrap();
558 assert_eq!(a.event_type, "a");
559 assert_eq!(b.event_type, "b");
560 assert_eq!(c.event_type, "c");
561 }
562
563 #[tokio::test]
566 async fn messages_returns_topic_history() {
567 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
568 let producer = broker.producer();
569 let _consumer = broker.consumer();
571
572 producer
573 .send(Message::new("t1", "a".to_string()))
574 .await
575 .unwrap();
576 producer
577 .send(Message::new("t1", "b".to_string()))
578 .await
579 .unwrap();
580 producer
581 .send(Message::new("t2", "c".to_string()))
582 .await
583 .unwrap();
584
585 let t1 = broker.messages("t1").await;
586 assert_eq!(t1.len(), 2);
587 assert_eq!(t1[0].payload, "a");
588 assert_eq!(t1[1].payload, "b");
589
590 let all = broker.all_messages().await;
591 assert_eq!(all.len(), 3);
592 }
593
594 #[tokio::test]
595 async fn in_memory_history_is_bounded() {
596 let broker = InMemoryBroker::with_history_limit(8, 2);
597 let producer = broker.producer();
598 let _consumer = broker.consumer();
599
600 producer.send(Message::new("events", 1_u32)).await.unwrap();
601 producer.send(Message::new("events", 2_u32)).await.unwrap();
602 producer.send(Message::new("events", 3_u32)).await.unwrap();
603
604 let messages = broker.messages("events").await;
605 assert_eq!(messages.len(), 2);
606 assert_eq!(messages[0].payload, 2);
607 assert_eq!(messages[1].payload, 3);
608 }
609
610 #[tokio::test]
611 async fn message_count_and_reset() {
612 let broker: InMemoryBroker<i32> = InMemoryBroker::new(16);
613 let producer = broker.producer();
614 let _consumer = broker.consumer();
615
616 assert_eq!(broker.message_count("t").await, 0);
617 producer.send(Message::new("t", 1)).await.unwrap();
618 assert_eq!(broker.message_count("t").await, 1);
619
620 broker.reset().await;
621 assert_eq!(broker.message_count("t").await, 0);
622 }
623
624 #[tokio::test]
625 async fn create_topic_and_topic_names() {
626 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
627 let _consumer = broker.consumer();
628
629 broker.create_topic("z-topic").await;
630 broker.create_topic("a-topic").await;
631
632 producer_send_helper(&broker, "m-topic").await;
633
634 let names = broker.topic_names().await;
635 assert_eq!(names, vec!["a-topic", "m-topic", "z-topic"]);
636 }
637
638 async fn producer_send_helper(broker: &InMemoryBroker<String>, topic: &str) {
640 let producer = broker.producer();
641 producer
642 .send(Message::new(topic, "x".to_string()))
643 .await
644 .unwrap();
645 }
646
647 #[tokio::test]
650 async fn test_assert_published() {
651 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
652 let producer = broker.producer();
653 let _consumer = broker.consumer();
654
655 producer
656 .send(Message::new("t1", "hello".to_string()))
657 .await
658 .unwrap();
659 producer
660 .send(Message::new("t1", "world".to_string()))
661 .await
662 .unwrap();
663
664 assert_published(&broker, "t1", |m| m.payload == "world").await;
665 }
666
667 #[tokio::test]
668 async fn test_assert_published_n() {
669 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
670 let producer = broker.producer();
671 let _consumer = broker.consumer();
672
673 producer
674 .send(Message::new("t1", "a".to_string()))
675 .await
676 .unwrap();
677 producer
678 .send(Message::new("t1", "b".to_string()))
679 .await
680 .unwrap();
681
682 assert_published_n(&broker, "t1", 2).await;
683 }
684
685 #[tokio::test]
686 async fn test_assert_no_messages() {
687 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
688 assert_no_messages(&broker, "empty-topic").await;
689 }
690
691 #[tokio::test]
692 async fn test_wait_for_message() {
693 let broker: InMemoryBroker<String> = InMemoryBroker::new(16);
694 let _consumer = broker.consumer();
695
696 let broker_clone = broker.clone();
697 tokio::spawn(async move {
698 tokio::time::sleep(Duration::from_millis(20)).await;
699 let producer = broker_clone.producer();
700 producer
701 .send(Message::new("t1", "delayed".to_string()))
702 .await
703 .unwrap();
704 });
705
706 let msg = wait_for_message(&broker, "t1", Duration::from_secs(2)).await;
707 assert_eq!(msg.payload, "delayed");
708 }
709 #[tokio::test]
710 async fn default_history_is_bounded_by_capacity() {
711 let broker: InMemoryBroker<usize> = InMemoryBroker::new(8);
712 let producer = broker.producer();
713 let _consumer = broker.consumer();
714
715 for value in 0..1030 {
716 producer.send(Message::new("history", value)).await.unwrap();
717 }
718
719 let messages = broker.messages("history").await;
720 assert_eq!(messages.len(), 8);
721 assert_eq!(messages.first().map(|msg| msg.payload), Some(1022));
722 }
723
724 #[tokio::test]
725 async fn bounded_history_limit_is_opt_in() {
726 let broker: InMemoryBroker<usize> = InMemoryBroker::with_history_limit(8, 2);
727 let producer = broker.producer();
728 let _consumer = broker.consumer();
729
730 for value in 0..4 {
731 producer.send(Message::new("history", value)).await.unwrap();
732 }
733
734 let payloads = broker
735 .messages("history")
736 .await
737 .into_iter()
738 .map(|msg| msg.payload)
739 .collect::<Vec<_>>();
740 assert_eq!(payloads, vec![2, 3]);
741 }
742
743 #[tokio::test]
744 async fn zero_capacity_is_clamped() {
745 let broker: InMemoryBroker<usize> = InMemoryBroker::new(0);
746 let producer = broker.producer();
747 let _consumer = broker.consumer();
748
749 producer.send(Message::new("history", 1)).await.unwrap();
750
751 let messages = broker.messages("history").await;
752 assert_eq!(messages.len(), 1);
753 }
754}