rskit_messaging/
registry.rs1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use rskit_errors::{AppError, AppResult, ErrorCode};
7
8use crate::config::BrokerConfig;
9use crate::traits::{MessageConsumer, MessageProducer};
10
11pub struct MessagingBackend<T: Send + Sync + 'static> {
13 pub producer: Arc<dyn MessageProducer<T>>,
15 pub consumer: Arc<dyn MessageConsumer<T>>,
17}
18
19pub trait MessagingFactory<T: Send + Sync + 'static>: Send + Sync {
21 fn create_producer(&self, config: &BrokerConfig) -> AppResult<Arc<dyn MessageProducer<T>>>;
23
24 fn create_consumer(&self, config: &BrokerConfig) -> AppResult<Arc<dyn MessageConsumer<T>>>;
26
27 fn create(&self, config: &BrokerConfig) -> AppResult<MessagingBackend<T>> {
29 Ok(MessagingBackend {
30 producer: self.create_producer(config)?,
31 consumer: self.create_consumer(config)?,
32 })
33 }
34}
35
36pub struct MessagingRegistry<T: Send + Sync + 'static> {
41 factories: BTreeMap<String, Arc<dyn MessagingFactory<T>>>,
42}
43
44impl<T: Send + Sync + 'static> MessagingRegistry<T> {
45 #[must_use]
47 pub fn new() -> Self {
48 Self {
49 factories: BTreeMap::new(),
50 }
51 }
52
53 pub fn register_backend(
55 &mut self,
56 adapter: impl Into<String>,
57 factory: Arc<dyn MessagingFactory<T>>,
58 ) -> AppResult<()> {
59 let adapter = adapter.into().trim().to_owned();
60 BrokerConfig::new(adapter.clone()).validate()?;
61 if self.factories.contains_key(&adapter) {
62 return Err(AppError::new(
63 ErrorCode::AlreadyExists,
64 format!("messaging adapter '{adapter}' is already registered"),
65 ));
66 }
67 self.factories.insert(adapter, factory);
68 Ok(())
69 }
70
71 pub fn build(&self, config: &BrokerConfig) -> AppResult<MessagingBackend<T>> {
73 config.validate()?;
74 let factory = self.factories.get(&config.adapter).ok_or_else(|| {
75 AppError::new(
76 ErrorCode::NotFound,
77 format!("messaging adapter '{}' is not registered", config.adapter),
78 )
79 })?;
80 factory.create(config)
81 }
82
83 pub fn producer(&self, config: &BrokerConfig) -> AppResult<Arc<dyn MessageProducer<T>>> {
85 config.validate()?;
86 let factory = self.factories.get(&config.adapter).ok_or_else(|| {
87 AppError::new(
88 ErrorCode::NotFound,
89 format!("messaging adapter '{}' is not registered", config.adapter),
90 )
91 })?;
92 factory.create_producer(config)
93 }
94
95 pub fn consumer(&self, config: &BrokerConfig) -> AppResult<Arc<dyn MessageConsumer<T>>> {
97 config.validate()?;
98 let factory = self.factories.get(&config.adapter).ok_or_else(|| {
99 AppError::new(
100 ErrorCode::NotFound,
101 format!("messaging adapter '{}' is not registered", config.adapter),
102 )
103 })?;
104 factory.create_consumer(config)
105 }
106
107 #[must_use]
109 pub fn adapters(&self) -> Vec<&str> {
110 self.factories.keys().map(String::as_str).collect()
111 }
112
113 #[must_use]
115 pub fn is_empty(&self) -> bool {
116 self.factories.is_empty()
117 }
118
119 #[must_use]
121 pub fn len(&self) -> usize {
122 self.factories.len()
123 }
124
125 #[must_use]
127 pub fn contains(&self, adapter: &str) -> bool {
128 self.factories.contains_key(adapter)
129 }
130}
131
132impl<T: Send + Sync + 'static> Default for MessagingRegistry<T> {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::memory::InMemoryBroker;
142 use crate::message::Message;
143 use async_trait::async_trait;
144 use std::sync::atomic::{AtomicUsize, Ordering};
145 use std::time::Duration;
146
147 #[test]
148 fn new_registry_is_empty_and_has_no_side_effects() {
149 let registry = MessagingRegistry::<String>::new();
150 assert!(registry.is_empty());
151 assert!(registry.adapters().is_empty());
152 assert!(registry.producer(&BrokerConfig::default()).is_err());
153 }
154
155 #[test]
156 fn rejects_duplicate_adapter() {
157 let mut registry = MessagingRegistry::<String>::new();
158 let broker = InMemoryBroker::new(8);
159 crate::memory::register(&mut registry, broker.clone()).unwrap();
160
161 let err = crate::memory::register(&mut registry, broker).unwrap_err();
162
163 assert_eq!(err.code(), ErrorCode::AlreadyExists);
164 }
165
166 #[test]
167 fn rejects_blank_adapter_registration() {
168 let mut registry = MessagingRegistry::<String>::new();
169 let factory = CountingFactory {
170 producer_calls: Arc::new(AtomicUsize::new(0)),
171 consumer_calls: Arc::new(AtomicUsize::new(0)),
172 };
173
174 let err = registry
175 .register_backend(" ", Arc::new(factory))
176 .expect_err("blank adapter must fail");
177
178 assert_eq!(err.code(), ErrorCode::InvalidInput);
179 }
180
181 #[test]
182 fn normalizes_adapter_registration_name() {
183 let mut registry = MessagingRegistry::<String>::new();
184 let factory = CountingFactory {
185 producer_calls: Arc::new(AtomicUsize::new(0)),
186 consumer_calls: Arc::new(AtomicUsize::new(0)),
187 };
188
189 registry
190 .register_backend(" counting ", Arc::new(factory))
191 .unwrap();
192
193 assert!(registry.contains("counting"));
194 assert!(!registry.contains(" counting "));
195 }
196
197 #[test]
198 fn producer_lookup_does_not_construct_consumer() {
199 let producer_calls = Arc::new(AtomicUsize::new(0));
200 let consumer_calls = Arc::new(AtomicUsize::new(0));
201 let factory = CountingFactory {
202 producer_calls: Arc::clone(&producer_calls),
203 consumer_calls: Arc::clone(&consumer_calls),
204 };
205 let mut registry = MessagingRegistry::<String>::new();
206 registry
207 .register_backend("counting", Arc::new(factory))
208 .unwrap();
209
210 let _producer = registry.producer(&BrokerConfig::new("counting")).unwrap();
211
212 assert_eq!(producer_calls.load(Ordering::SeqCst), 1);
213 assert_eq!(consumer_calls.load(Ordering::SeqCst), 0);
214 }
215
216 struct CountingFactory {
217 producer_calls: Arc<AtomicUsize>,
218 consumer_calls: Arc<AtomicUsize>,
219 }
220
221 impl MessagingFactory<String> for CountingFactory {
222 fn create_producer(
223 &self,
224 _config: &BrokerConfig,
225 ) -> AppResult<Arc<dyn MessageProducer<String>>> {
226 self.producer_calls.fetch_add(1, Ordering::SeqCst);
227 Ok(Arc::new(NoopProducer))
228 }
229
230 fn create_consumer(
231 &self,
232 _config: &BrokerConfig,
233 ) -> AppResult<Arc<dyn MessageConsumer<String>>> {
234 self.consumer_calls.fetch_add(1, Ordering::SeqCst);
235 Ok(Arc::new(NoopConsumer))
236 }
237 }
238
239 struct NoopProducer;
240
241 #[async_trait]
242 impl MessageProducer<String> for NoopProducer {
243 async fn send(&self, _msg: Message<String>) -> AppResult<()> {
244 Ok(())
245 }
246
247 async fn send_batch(&self, _msgs: Vec<Message<String>>) -> AppResult<()> {
248 Ok(())
249 }
250
251 async fn flush(&self, _timeout: Duration) -> AppResult<()> {
252 Ok(())
253 }
254 }
255
256 struct NoopConsumer;
257
258 #[async_trait]
259 impl MessageConsumer<String> for NoopConsumer {
260 async fn subscribe(&self, _topics: &[&str]) -> AppResult<()> {
261 Ok(())
262 }
263
264 async fn recv(&self) -> AppResult<Message<String>> {
265 Err(AppError::new(ErrorCode::NotFound, "no messages"))
266 }
267 }
268}