mq_bridge/endpoints/
switch.rs1use crate::traits::{MessagePublisher, PublisherError, Sent, SentBatch};
2use crate::CanonicalMessage;
3use async_trait::async_trait;
4use std::any::Any;
5use std::collections::HashMap;
6use std::sync::Arc;
7use tracing::warn;
8
9pub struct SwitchPublisher {
10 metadata_key: String,
11 cases: HashMap<String, Arc<dyn MessagePublisher>>,
12 default: Option<Arc<dyn MessagePublisher>>,
13}
14
15impl SwitchPublisher {
16 pub fn new(
17 metadata_key: String,
18 cases: HashMap<String, Arc<dyn MessagePublisher>>,
19 default: Option<Arc<dyn MessagePublisher>>,
20 ) -> Self {
21 Self {
22 metadata_key,
23 cases,
24 default,
25 }
26 }
27
28 fn get_publisher(&self, message: &CanonicalMessage) -> Option<&Arc<dyn MessagePublisher>> {
29 if let Some(val) = message.metadata.get(&self.metadata_key) {
30 if let Some(publisher) = self.cases.get(val) {
31 return Some(publisher);
32 }
33 }
34 self.default.as_ref()
35 }
36}
37
38#[async_trait]
39impl MessagePublisher for SwitchPublisher {
40 async fn send(&self, message: CanonicalMessage) -> Result<Sent, PublisherError> {
41 if let Some(publisher) = self.get_publisher(&message) {
42 publisher.send(message).await
43 } else {
44 warn!(
45 "Switch publisher dropped message with id {:032x}: metadata key '{}' not found or no matching case/default.",
46 message.message_id, self.metadata_key
47 );
48 Ok(Sent::Ack)
49 }
50 }
51
52 async fn send_batch(
53 &self,
54 messages: Vec<CanonicalMessage>,
55 ) -> Result<SentBatch, PublisherError> {
56 use futures::future::join_all;
57 use std::collections::HashMap;
58
59 if messages.is_empty() {
60 return Ok(SentBatch::Ack);
61 }
62
63 let mut grouped_messages: HashMap<
66 String,
67 (Arc<dyn MessagePublisher>, Vec<CanonicalMessage>),
68 > = HashMap::new();
69
70 for message in messages {
71 if let Some(publisher) = self.get_publisher(&message) {
72 grouped_messages
75 .entry(
76 message
77 .metadata
78 .get(&self.metadata_key)
79 .cloned()
80 .unwrap_or_default(),
81 )
82 .or_insert_with(|| (publisher.clone(), Vec::new()))
83 .1
84 .push(message);
85 } else {
86 warn!(
87 "Switch publisher dropped message with id {:032x}: metadata key '{}' not found or no matching case/default.",
88 message.message_id, self.metadata_key
89 );
90 }
91 }
92
93 let batch_sends = grouped_messages
95 .into_values()
96 .map(|(publisher, batch)| async move { publisher.send_batch(batch).await });
97
98 let results = join_all(batch_sends).await;
99
100 let mut all_responses = Vec::new();
102 let mut all_failed = Vec::new();
103
104 for result in results {
105 match result {
106 Ok(SentBatch::Ack) => {}
107 Ok(SentBatch::Partial { responses, failed }) => {
108 if let Some(resps) = responses {
109 all_responses.extend(resps);
110 }
111 all_failed.extend(failed);
112 }
113 Err(e) => {
114 return Err(e);
118 }
119 }
120 }
121
122 if all_failed.is_empty() && all_responses.is_empty() {
123 Ok(SentBatch::Ack)
124 } else {
125 Ok(SentBatch::Partial {
126 responses: if all_responses.is_empty() {
127 None
128 } else {
129 Some(all_responses)
130 },
131 failed: all_failed,
132 })
133 }
134 }
135
136 fn as_any(&self) -> &dyn Any {
137 self
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::endpoints::memory::MemoryPublisher;
145 use std::sync::Arc;
146
147 #[tokio::test]
148 async fn test_switch_publisher_routing() {
149 let pub_a = MemoryPublisher::new_local("topic_a", 10);
151 let pub_b = MemoryPublisher::new_local("topic_b", 10);
152 let pub_default = MemoryPublisher::new_local("topic_default", 10);
153
154 let chan_a = pub_a.channel();
155 let chan_b = pub_b.channel();
156 let chan_default = pub_default.channel();
157
158 let mut cases = HashMap::new();
159 cases.insert(
160 "A".to_string(),
161 Arc::new(pub_a) as Arc<dyn MessagePublisher>,
162 );
163 cases.insert(
164 "B".to_string(),
165 Arc::new(pub_b) as Arc<dyn MessagePublisher>,
166 );
167
168 let switch = SwitchPublisher::new(
169 "route_key".to_string(),
170 cases,
171 Some(Arc::new(pub_default) as Arc<dyn MessagePublisher>),
172 );
173
174 let msg_a = CanonicalMessage::from("payload_a").with_metadata_kv("route_key", "A");
176 switch.send(msg_a).await.unwrap();
177 assert_eq!(chan_a.len(), 1);
178 assert_eq!(chan_b.len(), 0);
179 assert_eq!(chan_default.len(), 0);
180 chan_a.drain_messages();
181
182 let msg_b = CanonicalMessage::from("payload_b").with_metadata_kv("route_key", "B");
184 switch.send(msg_b).await.unwrap();
185 assert_eq!(chan_a.len(), 0);
186 assert_eq!(chan_b.len(), 1);
187 assert_eq!(chan_default.len(), 0);
188 chan_b.drain_messages();
189
190 let msg_c =
192 CanonicalMessage::new(b"payload_c".to_vec(), None).with_metadata_kv("route_key", "C");
193 switch.send(msg_c).await.unwrap();
194 assert_eq!(chan_a.len(), 0);
195 assert_eq!(chan_b.len(), 0);
196 assert_eq!(chan_default.len(), 1);
197 chan_default.drain_messages();
198
199 let msg_d = CanonicalMessage::new(b"payload_d".to_vec(), None);
201 switch.send(msg_d).await.unwrap();
202 assert_eq!(chan_a.len(), 0);
203 assert_eq!(chan_b.len(), 0);
204 assert_eq!(chan_default.len(), 1);
205 }
206}