nautilus_common/factories/
client.rs1use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc};
25
26use ahash::AHashMap;
27use nautilus_model::identifiers::TraderId;
28
29use crate::{
30 cache::{Cache, CacheView},
31 clients::{DataClient, ExecutionClient},
32 clock::Clock,
33};
34
35pub trait ClientConfig: Debug {
40 fn as_any(&self) -> &dyn Any;
42}
43
44pub trait DataClientFactory: Debug {
49 fn create(
58 &self,
59 name: &str,
60 config: &dyn ClientConfig,
61 cache: CacheView,
62 clock: Rc<RefCell<dyn Clock>>,
63 ) -> anyhow::Result<Box<dyn DataClient>>;
64
65 fn name(&self) -> &str;
67
68 fn config_type(&self) -> &str;
70}
71
72pub trait ExecutionClientFactory: Debug {
77 fn create(
86 &self,
87 trader_id: TraderId,
88 name: &str,
89 config: &dyn ClientConfig,
90 cache: CacheView,
91 clock: Rc<RefCell<dyn Clock>>,
92 ) -> anyhow::Result<Box<dyn ExecutionClient>>;
93
94 fn name(&self) -> &str;
96
97 fn config_type(&self) -> &str;
99}
100
101pub trait SimulatedExecutionClientFactory: Debug {
106 fn create(
112 &self,
113 trader_id: TraderId,
114 name: &str,
115 config: &dyn ClientConfig,
116 cache: Rc<RefCell<Cache>>,
117 ) -> anyhow::Result<Box<dyn ExecutionClient>>;
118
119 fn name(&self) -> &str;
121
122 fn config_type(&self) -> &str;
124}
125
126#[derive(Debug, Default)]
131pub struct DataClientFactoryRegistry {
132 factories: AHashMap<String, Box<dyn DataClientFactory>>,
133}
134
135impl DataClientFactoryRegistry {
136 #[must_use]
138 pub fn new() -> Self {
139 Self {
140 factories: AHashMap::new(),
141 }
142 }
143
144 pub fn register(
150 &mut self,
151 name: String,
152 factory: Box<dyn DataClientFactory>,
153 ) -> anyhow::Result<()> {
154 if self.factories.contains_key(&name) {
155 anyhow::bail!("Data client factory '{name}' is already registered");
156 }
157
158 self.factories.insert(name, factory);
159 Ok(())
160 }
161
162 #[must_use]
168 pub fn get(&self, name: &str) -> Option<&dyn DataClientFactory> {
169 self.factories.get(name).map(std::convert::AsRef::as_ref)
170 }
171
172 #[must_use]
174 pub fn names(&self) -> Vec<&String> {
175 self.factories.keys().collect()
176 }
177
178 #[must_use]
180 pub fn contains(&self, name: &str) -> bool {
181 self.factories.contains_key(name)
182 }
183}
184
185#[derive(Debug, Default)]
190pub struct ExecutionClientFactoryRegistry {
191 factories: AHashMap<String, Box<dyn ExecutionClientFactory>>,
192}
193
194impl ExecutionClientFactoryRegistry {
195 #[must_use]
197 pub fn new() -> Self {
198 Self {
199 factories: AHashMap::new(),
200 }
201 }
202
203 pub fn register(
209 &mut self,
210 name: String,
211 factory: Box<dyn ExecutionClientFactory>,
212 ) -> anyhow::Result<()> {
213 if self.factories.contains_key(&name) {
214 anyhow::bail!("Execution client factory '{name}' is already registered");
215 }
216
217 self.factories.insert(name, factory);
218 Ok(())
219 }
220
221 #[must_use]
223 pub fn get(&self, name: &str) -> Option<&dyn ExecutionClientFactory> {
224 self.factories.get(name).map(std::convert::AsRef::as_ref)
225 }
226
227 #[must_use]
229 pub fn names(&self) -> Vec<&String> {
230 self.factories.keys().collect()
231 }
232
233 #[must_use]
235 pub fn contains(&self, name: &str) -> bool {
236 self.factories.contains_key(name)
237 }
238}
239
240#[allow(dead_code)]
241#[cfg(test)]
242mod tests {
243 use std::any::Any;
244
245 use rstest::*;
246
247 use super::*;
248
249 #[derive(Debug)]
250 struct MockConfig {
251 #[allow(dead_code)]
252 value: String,
253 }
254
255 impl ClientConfig for MockConfig {
256 fn as_any(&self) -> &dyn Any {
257 self
258 }
259 }
260
261 #[derive(Debug)]
262 struct MockDataClientFactory;
263
264 impl DataClientFactory for MockDataClientFactory {
265 fn create(
266 &self,
267 _name: &str,
268 _config: &dyn ClientConfig,
269 _cache: CacheView,
270 _clock: Rc<RefCell<dyn Clock>>,
271 ) -> anyhow::Result<Box<dyn DataClient>> {
272 Err(anyhow::anyhow!("Mock factory - not implemented"))
273 }
274
275 fn name(&self) -> &'static str {
276 "mock"
277 }
278
279 fn config_type(&self) -> &'static str {
280 "MockConfig"
281 }
282 }
283
284 #[derive(Debug)]
285 struct MockExecutionClientFactory;
286
287 impl ExecutionClientFactory for MockExecutionClientFactory {
288 fn create(
289 &self,
290 _trader_id: TraderId,
291 _name: &str,
292 _config: &dyn ClientConfig,
293 _cache: CacheView,
294 _clock: Rc<RefCell<dyn Clock>>,
295 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
296 Err(anyhow::anyhow!("Mock factory - not implemented"))
297 }
298
299 fn name(&self) -> &'static str {
300 "mock-exec"
301 }
302
303 fn config_type(&self) -> &'static str {
304 "MockConfig"
305 }
306 }
307
308 #[rstest]
309 fn test_data_client_factory_registry() {
310 let mut registry = DataClientFactoryRegistry::new();
311
312 assert!(registry.names().is_empty());
313 assert!(!registry.contains("mock"));
314 assert!(registry.get("mock").is_none());
315
316 let factory = Box::new(MockDataClientFactory);
317 registry.register("mock".to_string(), factory).unwrap();
318
319 assert_eq!(registry.names().len(), 1);
320 assert!(registry.contains("mock"));
321 assert!(registry.get("mock").is_some());
322
323 let factory2 = Box::new(MockDataClientFactory);
324 let error = registry.register("mock".to_string(), factory2).unwrap_err();
325 assert_eq!(
326 error.to_string(),
327 "Data client factory 'mock' is already registered"
328 );
329 assert_eq!(
330 registry.names().len(),
331 1,
332 "rejected registration must not be stored"
333 );
334 }
335
336 #[rstest]
337 fn test_execution_client_factory_registry() {
338 let mut registry = ExecutionClientFactoryRegistry::new();
339
340 assert!(registry.names().is_empty());
341 assert!(!registry.contains("mock-exec"));
342 assert!(registry.get("mock-exec").is_none());
343
344 registry
345 .register(
346 "mock-exec".to_string(),
347 Box::new(MockExecutionClientFactory),
348 )
349 .unwrap();
350
351 assert_eq!(registry.names(), vec![&"mock-exec".to_string()]);
352 assert!(registry.contains("mock-exec"));
353 assert_eq!(registry.get("mock-exec").unwrap().name(), "mock-exec");
354 assert_eq!(
355 registry.get("mock-exec").unwrap().config_type(),
356 "MockConfig"
357 );
358
359 let error = registry
360 .register(
361 "mock-exec".to_string(),
362 Box::new(MockExecutionClientFactory),
363 )
364 .unwrap_err();
365
366 assert_eq!(
367 error.to_string(),
368 "Execution client factory 'mock-exec' is already registered"
369 );
370 assert_eq!(
371 registry.names().len(),
372 1,
373 "rejected registration must not be stored"
374 );
375 }
376
377 #[rstest]
378 fn test_registries_do_not_match_unregistered_names() {
379 let mut data_registry = DataClientFactoryRegistry::new();
380 let mut execution_registry = ExecutionClientFactoryRegistry::new();
381
382 data_registry
383 .register("mock".to_string(), Box::new(MockDataClientFactory))
384 .unwrap();
385 execution_registry
386 .register(
387 "mock-exec".to_string(),
388 Box::new(MockExecutionClientFactory),
389 )
390 .unwrap();
391
392 assert!(!data_registry.contains("mock-exec"));
393 assert!(data_registry.get("mock-exec").is_none());
394 assert!(!execution_registry.contains("mock"));
395 assert!(execution_registry.get("mock").is_none());
396 }
397
398 #[rstest]
399 fn test_default_registries_are_empty() {
400 assert!(DataClientFactoryRegistry::default().names().is_empty());
401 assert!(ExecutionClientFactoryRegistry::default().names().is_empty());
402 }
403}