use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc};
use ahash::AHashMap;
use nautilus_common::{
cache::Cache,
clients::{DataClient, ExecutionClient},
clock::Clock,
};
pub trait ClientConfig: Debug {
fn as_any(&self) -> &dyn Any;
}
pub trait DataClientFactory: Debug {
fn create(
&self,
name: &str,
config: &dyn ClientConfig,
cache: Rc<RefCell<Cache>>,
clock: Rc<RefCell<dyn Clock>>,
) -> anyhow::Result<Box<dyn DataClient>>;
fn name(&self) -> &str;
fn config_type(&self) -> &str;
}
pub trait ExecutionClientFactory: Debug {
fn create(
&self,
name: &str,
config: &dyn ClientConfig,
cache: Rc<RefCell<Cache>>,
) -> anyhow::Result<Box<dyn ExecutionClient>>;
fn name(&self) -> &str;
fn config_type(&self) -> &str;
}
#[derive(Debug, Default)]
pub struct DataClientFactoryRegistry {
factories: AHashMap<String, Box<dyn DataClientFactory>>,
}
impl DataClientFactoryRegistry {
#[must_use]
pub fn new() -> Self {
Self {
factories: AHashMap::new(),
}
}
pub fn register(
&mut self,
name: String,
factory: Box<dyn DataClientFactory>,
) -> anyhow::Result<()> {
if self.factories.contains_key(&name) {
anyhow::bail!("Data client factory '{name}' is already registered");
}
self.factories.insert(name, factory);
Ok(())
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&dyn DataClientFactory> {
self.factories.get(name).map(std::convert::AsRef::as_ref)
}
#[must_use]
pub fn names(&self) -> Vec<&String> {
self.factories.keys().collect()
}
#[must_use]
pub fn contains(&self, name: &str) -> bool {
self.factories.contains_key(name)
}
}
#[derive(Debug, Default)]
pub struct ExecutionClientFactoryRegistry {
factories: AHashMap<String, Box<dyn ExecutionClientFactory>>,
}
impl ExecutionClientFactoryRegistry {
#[must_use]
pub fn new() -> Self {
Self {
factories: AHashMap::new(),
}
}
pub fn register(
&mut self,
name: String,
factory: Box<dyn ExecutionClientFactory>,
) -> anyhow::Result<()> {
if self.factories.contains_key(&name) {
anyhow::bail!("Execution client factory '{name}' is already registered");
}
self.factories.insert(name, factory);
Ok(())
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&dyn ExecutionClientFactory> {
self.factories.get(name).map(std::convert::AsRef::as_ref)
}
#[must_use]
pub fn names(&self) -> Vec<&String> {
self.factories.keys().collect()
}
#[must_use]
pub fn contains(&self, name: &str) -> bool {
self.factories.contains_key(name)
}
}
#[allow(dead_code)]
#[cfg(test)]
mod tests {
use std::any::Any;
use rstest::*;
use super::*;
#[derive(Debug)]
struct MockConfig {
#[allow(dead_code)]
value: String,
}
impl ClientConfig for MockConfig {
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
struct MockDataClientFactory;
impl DataClientFactory for MockDataClientFactory {
fn create(
&self,
_name: &str,
_config: &dyn ClientConfig,
_cache: Rc<RefCell<Cache>>,
_clock: Rc<RefCell<dyn Clock>>,
) -> anyhow::Result<Box<dyn DataClient>> {
Err(anyhow::anyhow!("Mock factory - not implemented"))
}
fn name(&self) -> &'static str {
"mock"
}
fn config_type(&self) -> &'static str {
"MockConfig"
}
}
#[rstest]
fn test_data_client_factory_registry() {
let mut registry = DataClientFactoryRegistry::new();
assert!(registry.names().is_empty());
assert!(!registry.contains("mock"));
assert!(registry.get("mock").is_none());
let factory = Box::new(MockDataClientFactory);
registry.register("mock".to_string(), factory).unwrap();
assert_eq!(registry.names().len(), 1);
assert!(registry.contains("mock"));
assert!(registry.get("mock").is_some());
let factory2 = Box::new(MockDataClientFactory);
let result = registry.register("mock".to_string(), factory2);
assert!(result.is_err());
}
#[rstest]
fn test_empty_data_client_factory_registry() {
let registry = DataClientFactoryRegistry::new();
assert!(registry.names().is_empty());
assert!(!registry.contains("mock"));
assert!(registry.get("mock").is_none());
}
#[rstest]
fn test_empty_execution_client_factory_registry() {
let registry = ExecutionClientFactoryRegistry::new();
assert!(registry.names().is_empty());
assert!(!registry.contains("mock"));
assert!(registry.get("mock").is_none());
}
}