use crate::{BackendConfig, BackendFactory, BackendVersion, CanBackend, CanError, CanResult};
use indexmap::IndexMap;
use std::sync::{Arc, OnceLock, RwLock};
#[derive(Debug, Clone)]
pub struct BackendInfo {
pub name: String,
pub version: BackendVersion,
}
pub struct BackendRegistry {
factories: RwLock<IndexMap<String, Arc<dyn BackendFactory>>>,
}
impl BackendRegistry {
#[must_use]
pub fn new() -> Self {
Self {
factories: RwLock::new(IndexMap::new()),
}
}
pub fn global() -> Arc<Self> {
static INSTANCE: OnceLock<Arc<BackendRegistry>> = OnceLock::new();
INSTANCE.get_or_init(|| Arc::new(Self::new())).clone()
}
pub fn register(&self, factory: Arc<dyn BackendFactory>) -> CanResult<()> {
let name = factory.name().to_string();
let mut factories = self.factories.write().map_err(|e| CanError::Other {
message: format!("Failed to acquire write lock: {e}"),
})?;
if factories.contains_key(&name) {
return Err(CanError::BackendAlreadyRegistered { name: name.clone() });
}
factories.insert(name, factory);
Ok(())
}
pub fn unregister(&self, name: &str) -> CanResult<()> {
let mut factories = self.factories.write().map_err(|e| CanError::Other {
message: format!("Failed to acquire write lock: {e}"),
})?;
if factories.shift_remove(name).is_none() {
return Err(CanError::BackendNotFound {
name: name.to_string(),
});
}
Ok(())
}
pub fn create(&self, name: &str, config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
let factories = self.factories.read().map_err(|e| CanError::Other {
message: format!("Failed to acquire read lock: {e}"),
})?;
let factory = factories
.get(name)
.ok_or_else(|| CanError::BackendNotFound {
name: name.to_string(),
})?;
factory.create(config)
}
pub fn list_backends(&self) -> Vec<String> {
let factories = self.factories.read().unwrap();
factories.keys().cloned().collect()
}
pub fn get_backend_info(&self, name: &str) -> CanResult<BackendInfo> {
let factories = self.factories.read().map_err(|e| CanError::Other {
message: format!("Failed to acquire read lock: {e}"),
})?;
let factory = factories
.get(name)
.ok_or_else(|| CanError::BackendNotFound {
name: name.to_string(),
})?;
Ok(BackendInfo {
name: factory.name().to_string(),
version: factory.version(),
})
}
#[must_use]
pub fn is_registered(&self, name: &str) -> bool {
let factories = self.factories.read().unwrap();
factories.contains_key(name)
}
}
impl Default for BackendRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CanMessage, HardwareCapability};
struct MockBackend;
impl CanBackend for MockBackend {
fn initialize(&mut self, _config: &BackendConfig) -> CanResult<()> {
Ok(())
}
fn close(&mut self) -> CanResult<()> {
Ok(())
}
fn get_capability(&self) -> CanResult<HardwareCapability> {
Ok(HardwareCapability::new(
2,
true,
8_000_000,
vec![125_000, 250_000, 500_000, 1_000_000],
16,
crate::TimestampPrecision::Microsecond,
))
}
fn send_message(&mut self, _message: &CanMessage) -> CanResult<()> {
Ok(())
}
fn receive_message(&mut self) -> CanResult<Option<CanMessage>> {
Ok(None)
}
fn open_channel(&mut self, _channel: u8) -> CanResult<()> {
Ok(())
}
fn close_channel(&mut self, _channel: u8) -> CanResult<()> {
Ok(())
}
fn version(&self) -> BackendVersion {
BackendVersion::new(0, 1, 0)
}
fn name(&self) -> &'static str {
"mock"
}
}
struct MockBackendFactory;
impl BackendFactory for MockBackendFactory {
fn create(&self, _config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
Ok(Box::new(MockBackend))
}
fn name(&self) -> &'static str {
"mock"
}
fn version(&self) -> BackendVersion {
BackendVersion::new(0, 1, 0)
}
}
#[test]
fn test_registry_new() {
let registry = BackendRegistry::new();
assert_eq!(registry.list_backends().len(), 0);
}
#[test]
fn test_registry_register() {
let registry = BackendRegistry::new();
let factory = Arc::new(MockBackendFactory);
assert!(registry.register(factory).is_ok());
assert_eq!(registry.list_backends().len(), 1);
assert!(registry.is_registered("mock"));
}
#[test]
fn test_registry_register_duplicate() {
let registry = BackendRegistry::new();
let factory1 = Arc::new(MockBackendFactory);
let factory2 = Arc::new(MockBackendFactory);
assert!(registry.register(factory1).is_ok());
assert!(registry.register(factory2).is_err());
}
#[test]
fn test_registry_unregister() {
let registry = BackendRegistry::new();
let factory = Arc::new(MockBackendFactory);
registry.register(factory).unwrap();
assert!(registry.is_registered("mock"));
assert!(registry.unregister("mock").is_ok());
assert!(!registry.is_registered("mock"));
}
#[test]
fn test_registry_unregister_not_found() {
let registry = BackendRegistry::new();
assert!(registry.unregister("nonexistent").is_err());
}
#[test]
fn test_registry_create() {
let registry = BackendRegistry::new();
let factory = Arc::new(MockBackendFactory);
let config = BackendConfig::new("mock");
registry.register(factory).unwrap();
let backend = registry.create("mock", &config);
assert!(backend.is_ok());
}
#[test]
fn test_registry_create_not_found() {
let registry = BackendRegistry::new();
let config = BackendConfig::new("mock");
let result = registry.create("nonexistent", &config);
assert!(result.is_err());
}
#[test]
fn test_registry_list_backends() {
let registry = BackendRegistry::new();
let factory = Arc::new(MockBackendFactory);
registry.register(factory).unwrap();
let backends = registry.list_backends();
assert_eq!(backends.len(), 1);
assert!(backends.contains(&"mock".to_string()));
}
#[test]
fn test_registry_get_backend_info() {
let registry = BackendRegistry::new();
let factory = Arc::new(MockBackendFactory);
registry.register(factory).unwrap();
let info = registry.get_backend_info("mock").unwrap();
assert_eq!(info.name, "mock");
assert_eq!(info.version.major(), 0);
assert_eq!(info.version.minor(), 1);
assert_eq!(info.version.patch(), 0);
}
#[test]
fn test_registry_global() {
let registry1 = BackendRegistry::global();
let registry2 = BackendRegistry::global();
assert!(Arc::ptr_eq(®istry1, ®istry2));
}
#[test]
fn test_registration_order() {
struct TestFactory(&'static str);
impl BackendFactory for TestFactory {
fn create(&self, _config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
Ok(Box::new(MockBackend))
}
fn name(&self) -> &str {
self.0
}
fn version(&self) -> BackendVersion {
BackendVersion::new(0, 1, 0)
}
}
let registry = BackendRegistry::new();
registry
.register(Arc::new(TestFactory("backend_a")))
.unwrap();
registry
.register(Arc::new(TestFactory("backend_b")))
.unwrap();
registry
.register(Arc::new(TestFactory("backend_c")))
.unwrap();
let backends = registry.list_backends();
assert_eq!(backends, vec!["backend_a", "backend_b", "backend_c"]);
}
#[test]
fn test_duplicate_registration_error_type() {
let registry = BackendRegistry::new();
let factory1 = Arc::new(MockBackendFactory);
let factory2 = Arc::new(MockBackendFactory);
registry.register(factory1).unwrap();
let result = registry.register(factory2);
assert!(result.is_err());
match result.unwrap_err() {
CanError::BackendAlreadyRegistered { name } => {
assert_eq!(name, "mock");
}
other => panic!("Expected BackendAlreadyRegistered, got {other:?}"),
}
}
}