Skip to main content

canlink_hal/
registry.rs

1//! Backend registry for managing and discovering hardware backends.
2//!
3//! This module provides the `BackendRegistry` for registering, querying, and creating
4//! backend instances at runtime.
5
6use crate::{BackendConfig, BackendFactory, BackendVersion, CanBackend, CanError, CanResult};
7use indexmap::IndexMap;
8use std::sync::{Arc, OnceLock, RwLock};
9
10/// Backend information.
11///
12/// Contains metadata about a registered backend.
13///
14/// # Examples
15///
16/// ```
17/// use canlink_hal::BackendInfo;
18///
19/// let info = BackendInfo {
20///     name: "mock".to_string(),
21///     version: canlink_hal::BackendVersion::new(0, 1, 0),
22/// };
23/// println!("Backend: {} v{}", info.name, info.version);
24/// ```
25#[derive(Debug, Clone)]
26pub struct BackendInfo {
27    /// Backend name
28    pub name: String,
29
30    /// Backend version
31    pub version: BackendVersion,
32}
33
34/// Backend registry.
35///
36/// Manages all registered hardware backends and provides methods for registration,
37/// querying, and creating backend instances.
38///
39/// # Thread Safety
40///
41/// All methods of `BackendRegistry` are thread-safe and can be called from multiple
42/// threads simultaneously. Internal state is protected by `RwLock`.
43///
44/// # Examples
45///
46/// ```rust,ignore
47/// use canlink_hal::BackendRegistry;
48///
49/// // Register a backend
50/// let registry = BackendRegistry::new();
51/// registry.register(Box::new(MockBackendFactory::new()))?;
52///
53/// // Query available backends
54/// let backends = registry.list_backends();
55/// println!("Available backends: {:?}", backends);
56///
57/// // Create a backend instance
58/// let backend = registry.create("mock", &config)?;
59/// ```
60pub struct BackendRegistry {
61    factories: RwLock<IndexMap<String, Arc<dyn BackendFactory>>>,
62}
63
64impl BackendRegistry {
65    /// Create a new backend registry.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use canlink_hal::BackendRegistry;
71    ///
72    /// let registry = BackendRegistry::new();
73    /// ```
74    #[must_use]
75    pub fn new() -> Self {
76        Self {
77            factories: RwLock::new(IndexMap::new()),
78        }
79    }
80
81    /// Get the global registry instance (singleton).
82    ///
83    /// This returns a shared reference to the global registry. The global registry
84    /// is initialized on first access and persists for the lifetime of the program.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use canlink_hal::BackendRegistry;
90    ///
91    /// let registry = BackendRegistry::global();
92    /// ```
93    pub fn global() -> Arc<Self> {
94        static INSTANCE: OnceLock<Arc<BackendRegistry>> = OnceLock::new();
95        INSTANCE.get_or_init(|| Arc::new(Self::new())).clone()
96    }
97
98    /// Register a backend factory.
99    ///
100    /// Registers a new backend factory with the registry. The backend name must be unique.
101    ///
102    /// # Arguments
103    ///
104    /// * `factory` - Backend factory instance
105    ///
106    /// # Errors
107    ///
108    /// * `CanError::BackendAlreadyRegistered` - Backend name already registered
109    ///
110    /// # Thread Safety
111    ///
112    /// This method is thread-safe and can be called from multiple threads.
113    ///
114    /// # Examples
115    ///
116    /// ```rust,ignore
117    /// registry.register(Box::new(MockBackendFactory::new()))?;
118    /// ```
119    pub fn register(&self, factory: Arc<dyn BackendFactory>) -> CanResult<()> {
120        let name = factory.name().to_string();
121        let mut factories = self.factories.write().map_err(|e| CanError::Other {
122            message: format!("Failed to acquire write lock: {e}"),
123        })?;
124
125        if factories.contains_key(&name) {
126            return Err(CanError::BackendAlreadyRegistered { name: name.clone() });
127        }
128
129        factories.insert(name, factory);
130        Ok(())
131    }
132
133    /// Unregister a backend.
134    ///
135    /// Removes a backend factory from the registry.
136    ///
137    /// # Arguments
138    ///
139    /// * `name` - Backend name
140    ///
141    /// # Errors
142    ///
143    /// * `CanError::BackendNotFound` - Backend not registered
144    ///
145    /// # Examples
146    ///
147    /// ```rust,ignore
148    /// registry.unregister("mock")?;
149    /// ```
150    pub fn unregister(&self, name: &str) -> CanResult<()> {
151        let mut factories = self.factories.write().map_err(|e| CanError::Other {
152            message: format!("Failed to acquire write lock: {e}"),
153        })?;
154
155        if factories.shift_remove(name).is_none() {
156            return Err(CanError::BackendNotFound {
157                name: name.to_string(),
158            });
159        }
160
161        Ok(())
162    }
163
164    /// Create a backend instance.
165    ///
166    /// Creates a new backend instance using the registered factory.
167    ///
168    /// # Arguments
169    ///
170    /// * `name` - Backend name
171    /// * `config` - Backend configuration
172    ///
173    /// # Returns
174    ///
175    /// A boxed backend instance ready for initialization.
176    ///
177    /// # Errors
178    ///
179    /// * `CanError::BackendNotFound` - Backend not registered
180    /// * `CanError::ConfigError` - Invalid configuration
181    ///
182    /// # Thread Safety
183    ///
184    /// This method is thread-safe and can be called from multiple threads.
185    ///
186    /// # Examples
187    ///
188    /// ```rust,ignore
189    /// let backend = registry.create("mock", &config)?;
190    /// ```
191    pub fn create(&self, name: &str, config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
192        let factories = self.factories.read().map_err(|e| CanError::Other {
193            message: format!("Failed to acquire read lock: {e}"),
194        })?;
195
196        let factory = factories
197            .get(name)
198            .ok_or_else(|| CanError::BackendNotFound {
199                name: name.to_string(),
200            })?;
201
202        factory.create(config)
203    }
204
205    /// List all registered backends.
206    ///
207    /// Returns a list of all backend names currently registered, in registration order.
208    /// Backends are returned in the order they were registered, with earlier registrations
209    /// appearing first in the list.
210    ///
211    /// # Panics
212    ///
213    /// Panics if the internal lock is poisoned (should never happen in normal operation).
214    ///
215    /// # Examples
216    ///
217    /// ```rust,ignore
218    /// let backends = registry.list_backends();
219    /// for name in backends {
220    ///     println!("Available: {}", name);
221    /// }
222    /// ```
223    pub fn list_backends(&self) -> Vec<String> {
224        let factories = self.factories.read().unwrap();
225        // IndexMap preserves insertion order
226        factories.keys().cloned().collect()
227    }
228
229    /// Get backend information.
230    ///
231    /// Returns metadata about a registered backend.
232    ///
233    /// # Arguments
234    ///
235    /// * `name` - Backend name
236    ///
237    /// # Errors
238    ///
239    /// * `CanError::BackendNotFound` - Backend not registered
240    ///
241    /// # Examples
242    ///
243    /// ```rust,ignore
244    /// let info = registry.get_backend_info("mock")?;
245    /// println!("Backend: {} v{}", info.name, info.version);
246    /// ```
247    pub fn get_backend_info(&self, name: &str) -> CanResult<BackendInfo> {
248        let factories = self.factories.read().map_err(|e| CanError::Other {
249            message: format!("Failed to acquire read lock: {e}"),
250        })?;
251
252        let factory = factories
253            .get(name)
254            .ok_or_else(|| CanError::BackendNotFound {
255                name: name.to_string(),
256            })?;
257
258        Ok(BackendInfo {
259            name: factory.name().to_string(),
260            version: factory.version(),
261        })
262    }
263
264    /// Check if a backend is registered.
265    ///
266    /// # Arguments
267    ///
268    /// * `name` - Backend name
269    ///
270    /// # Returns
271    ///
272    /// `true` if the backend is registered, `false` otherwise.
273    ///
274    /// # Panics
275    ///
276    /// Panics if the internal lock is poisoned (should never happen in normal operation).
277    ///
278    /// # Examples
279    ///
280    /// ```rust,ignore
281    /// if registry.is_registered("mock") {
282    ///     println!("Mock backend is available");
283    /// }
284    /// ```
285    #[must_use]
286    pub fn is_registered(&self, name: &str) -> bool {
287        let factories = self.factories.read().unwrap();
288        factories.contains_key(name)
289    }
290}
291
292impl Default for BackendRegistry {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301    use crate::{CanMessage, HardwareCapability};
302
303    // Mock backend for testing
304    struct MockBackend;
305
306    impl CanBackend for MockBackend {
307        fn initialize(&mut self, _config: &BackendConfig) -> CanResult<()> {
308            Ok(())
309        }
310
311        fn close(&mut self) -> CanResult<()> {
312            Ok(())
313        }
314
315        fn get_capability(&self) -> CanResult<HardwareCapability> {
316            Ok(HardwareCapability::new(
317                2,
318                true,
319                8_000_000,
320                vec![125_000, 250_000, 500_000, 1_000_000],
321                16,
322                crate::TimestampPrecision::Microsecond,
323            ))
324        }
325
326        fn send_message(&mut self, _message: &CanMessage) -> CanResult<()> {
327            Ok(())
328        }
329
330        fn receive_message(&mut self) -> CanResult<Option<CanMessage>> {
331            Ok(None)
332        }
333
334        fn open_channel(&mut self, _channel: u8) -> CanResult<()> {
335            Ok(())
336        }
337
338        fn close_channel(&mut self, _channel: u8) -> CanResult<()> {
339            Ok(())
340        }
341
342        fn version(&self) -> BackendVersion {
343            BackendVersion::new(0, 1, 0)
344        }
345
346        fn name(&self) -> &'static str {
347            "mock"
348        }
349    }
350
351    struct MockBackendFactory;
352
353    impl BackendFactory for MockBackendFactory {
354        fn create(&self, _config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
355            Ok(Box::new(MockBackend))
356        }
357
358        fn name(&self) -> &'static str {
359            "mock"
360        }
361
362        fn version(&self) -> BackendVersion {
363            BackendVersion::new(0, 1, 0)
364        }
365    }
366
367    #[test]
368    fn test_registry_new() {
369        let registry = BackendRegistry::new();
370        assert_eq!(registry.list_backends().len(), 0);
371    }
372
373    #[test]
374    fn test_registry_register() {
375        let registry = BackendRegistry::new();
376        let factory = Arc::new(MockBackendFactory);
377
378        assert!(registry.register(factory).is_ok());
379        assert_eq!(registry.list_backends().len(), 1);
380        assert!(registry.is_registered("mock"));
381    }
382
383    #[test]
384    fn test_registry_register_duplicate() {
385        let registry = BackendRegistry::new();
386        let factory1 = Arc::new(MockBackendFactory);
387        let factory2 = Arc::new(MockBackendFactory);
388
389        assert!(registry.register(factory1).is_ok());
390        assert!(registry.register(factory2).is_err());
391    }
392
393    #[test]
394    fn test_registry_unregister() {
395        let registry = BackendRegistry::new();
396        let factory = Arc::new(MockBackendFactory);
397
398        registry.register(factory).unwrap();
399        assert!(registry.is_registered("mock"));
400
401        assert!(registry.unregister("mock").is_ok());
402        assert!(!registry.is_registered("mock"));
403    }
404
405    #[test]
406    fn test_registry_unregister_not_found() {
407        let registry = BackendRegistry::new();
408        assert!(registry.unregister("nonexistent").is_err());
409    }
410
411    #[test]
412    fn test_registry_create() {
413        let registry = BackendRegistry::new();
414        let factory = Arc::new(MockBackendFactory);
415        let config = BackendConfig::new("mock");
416
417        registry.register(factory).unwrap();
418
419        let backend = registry.create("mock", &config);
420        assert!(backend.is_ok());
421    }
422
423    #[test]
424    fn test_registry_create_not_found() {
425        let registry = BackendRegistry::new();
426        let config = BackendConfig::new("mock");
427
428        let result = registry.create("nonexistent", &config);
429        assert!(result.is_err());
430    }
431
432    #[test]
433    fn test_registry_list_backends() {
434        let registry = BackendRegistry::new();
435        let factory = Arc::new(MockBackendFactory);
436
437        registry.register(factory).unwrap();
438
439        let backends = registry.list_backends();
440        assert_eq!(backends.len(), 1);
441        assert!(backends.contains(&"mock".to_string()));
442    }
443
444    #[test]
445    fn test_registry_get_backend_info() {
446        let registry = BackendRegistry::new();
447        let factory = Arc::new(MockBackendFactory);
448
449        registry.register(factory).unwrap();
450
451        let info = registry.get_backend_info("mock").unwrap();
452        assert_eq!(info.name, "mock");
453        assert_eq!(info.version.major(), 0);
454        assert_eq!(info.version.minor(), 1);
455        assert_eq!(info.version.patch(), 0);
456    }
457
458    #[test]
459    fn test_registry_global() {
460        let registry1 = BackendRegistry::global();
461        let registry2 = BackendRegistry::global();
462
463        // Should be the same instance
464        assert!(Arc::ptr_eq(&registry1, &registry2));
465    }
466
467    #[test]
468    fn test_registration_order() {
469        // Create test factories with different names
470        struct TestFactory(&'static str);
471        impl BackendFactory for TestFactory {
472            fn create(&self, _config: &BackendConfig) -> CanResult<Box<dyn CanBackend>> {
473                Ok(Box::new(MockBackend))
474            }
475            fn name(&self) -> &str {
476                self.0
477            }
478            fn version(&self) -> BackendVersion {
479                BackendVersion::new(0, 1, 0)
480            }
481        }
482
483        let registry = BackendRegistry::new();
484
485        // Register in specific order
486        registry
487            .register(Arc::new(TestFactory("backend_a")))
488            .unwrap();
489        registry
490            .register(Arc::new(TestFactory("backend_b")))
491            .unwrap();
492        registry
493            .register(Arc::new(TestFactory("backend_c")))
494            .unwrap();
495
496        // Verify order is preserved
497        let backends = registry.list_backends();
498        assert_eq!(backends, vec!["backend_a", "backend_b", "backend_c"]);
499    }
500
501    #[test]
502    fn test_duplicate_registration_error_type() {
503        let registry = BackendRegistry::new();
504        let factory1 = Arc::new(MockBackendFactory);
505        let factory2 = Arc::new(MockBackendFactory);
506
507        registry.register(factory1).unwrap();
508
509        let result = registry.register(factory2);
510        assert!(result.is_err());
511
512        // Verify error type
513        match result.unwrap_err() {
514            CanError::BackendAlreadyRegistered { name } => {
515                assert_eq!(name, "mock");
516            }
517            other => panic!("Expected BackendAlreadyRegistered, got {other:?}"),
518        }
519    }
520}