Skip to main content

horon_engine/
registry.rs

1//! registry.rs - ComponentRegistry for module independence
2//!
3//! This module implements a flexible component registry system that enables
4//! HTT to function as a standalone library while also supporting integration
5//! with GSD. It provides a uniform interface for component registration,
6//! retrieval, and management across different runtime environments.
7//!
8//! The registry maintains type safety through runtime type checking and
9//! manages component lifecycles efficiently through reference counting
10//! and thread-safe access patterns.
11
12use std::any::Any;
13use std::collections::HashMap;
14use std::fmt::Debug;
15use std::sync::{Arc, RwLock};
16
17/// Error type for registry operations
18#[derive(Debug, Clone)]
19pub struct RegistryError {
20    message: String,
21}
22
23impl RegistryError {
24    /// Create a registry error with the given message.
25    pub fn new(message: &str) -> Self {
26        RegistryError {
27            message: message.to_string(),
28        }
29    }
30}
31
32impl std::fmt::Display for RegistryError {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "Registry error: {}", self.message)
35    }
36}
37
38impl std::error::Error for RegistryError {}
39
40/// ComponentRegistry trait for registering and retrieving components
41///
42/// This trait defines the interface for a component registry used by HTT
43/// to store and retrieve components. It's designed to be implemented by
44/// both the standalone HTT library and adapters for external systems like GSD.
45pub trait ComponentRegistry: Send + Sync {
46    /// Register a component with the given name
47    fn register<T: 'static + Send + Sync>(&mut self, name: &str, component: Arc<RwLock<T>>) -> Result<(), RegistryError>;
48    
49    /// Get a component by name
50    fn get<T: 'static + Send + Sync>(&self, name: &str) -> Option<Arc<RwLock<T>>>;
51    
52    /// Get a component by name for mutation.
53    ///
54    /// Returns the same `Arc<RwLock<T>>` as [`get`](Self::get); mutation goes
55    /// through the returned `RwLock`, so no separate exclusive handle is
56    /// needed. Retained as a distinct method for call-site intent.
57    fn get_mut<T: 'static + Send + Sync>(&self, name: &str) -> Option<Arc<RwLock<T>>>;
58    
59    /// Check if a component exists
60    fn contains(&self, name: &str) -> bool;
61}
62
63/// Standalone implementation of ComponentRegistry for use in HTT without GSD
64pub struct HTTComponentRegistry {
65    components: HashMap<String, Arc<RwLock<Box<dyn Any + Send + Sync>>>>,
66}
67
68impl Debug for HTTComponentRegistry {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.debug_struct("HTTComponentRegistry")
71            .field("components_count", &self.components.len())
72            .finish()
73    }
74}
75
76impl HTTComponentRegistry {
77    /// Create a new empty component registry
78    pub fn new() -> Self {
79        HTTComponentRegistry {
80            components: HashMap::new(),
81        }
82    }
83    
84    /// Get the number of registered components
85    pub fn len(&self) -> usize {
86        self.components.len()
87    }
88    
89    /// Check if the registry is empty
90    pub fn is_empty(&self) -> bool {
91        self.components.is_empty()
92    }
93}
94
95impl ComponentRegistry for HTTComponentRegistry {
96    fn register<T: 'static + Send + Sync>(&mut self, name: &str, component: Arc<RwLock<T>>) -> Result<(), RegistryError> {
97        // Store the Arc<RwLock<T>> as a boxed Any inside an Arc<RwLock<_>>
98        let boxed: Box<dyn Any + Send + Sync> = Box::new(component);
99        self.components.insert(name.to_string(), Arc::new(RwLock::new(boxed)));
100        Ok(())
101    }
102
103    fn get<T: 'static + Send + Sync>(&self, name: &str) -> Option<Arc<RwLock<T>>> {
104        self.components.get(name).and_then(|boxed_any| {
105            let guard = boxed_any.read().ok()?;
106            guard.downcast_ref::<Arc<RwLock<T>>>().cloned()
107        })
108    }
109
110    fn get_mut<T: 'static + Send + Sync>(&self, name: &str) -> Option<Arc<RwLock<T>>> {
111        // Mutation is via the returned RwLock; the handle is identical to get.
112        self.get(name)
113    }
114    
115    fn contains(&self, name: &str) -> bool {
116        self.components.contains_key(name)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    
124    // Mock component for testing
125    #[derive(Debug)]
126    struct TestComponent {
127        value: i32,
128    }
129    
130    #[test]
131    fn test_registry_operations() {
132        // Create registry
133        let mut registry = HTTComponentRegistry::new();
134        
135        // Register a component
136        let component = Arc::new(RwLock::new(TestComponent { value: 42 }));
137        registry.register("test", component).unwrap();
138        
139        // Check contains
140        assert!(registry.contains("test"));
141        assert!(!registry.contains("nonexistent"));
142        
143        // Get the component
144        let retrieved = registry.get::<TestComponent>("test").unwrap();
145        assert_eq!(retrieved.read().unwrap().value, 42);
146        
147        // Modify the component
148        {
149            let mut comp = retrieved.write().unwrap();
150            comp.value = 100;
151        }
152        
153        // Get it again to verify the change
154        let retrieved_again = registry.get::<TestComponent>("test").unwrap();
155        assert_eq!(retrieved_again.read().unwrap().value, 100);
156    }
157    
158    #[test]
159    fn test_multiple_components() {
160        // Create registry
161        let mut registry = HTTComponentRegistry::new();
162        
163        // Register multiple components
164        registry.register("comp1", Arc::new(RwLock::new(TestComponent { value: 1 }))).unwrap();
165        registry.register("comp2", Arc::new(RwLock::new(TestComponent { value: 2 }))).unwrap();
166        registry.register("comp3", Arc::new(RwLock::new(TestComponent { value: 3 }))).unwrap();
167        
168        // Check registry size
169        assert_eq!(registry.len(), 3);
170        
171        // Get all components
172        let comp1 = registry.get::<TestComponent>("comp1").unwrap();
173        let comp2 = registry.get::<TestComponent>("comp2").unwrap();
174        let comp3 = registry.get::<TestComponent>("comp3").unwrap();
175        
176        // Verify values
177        assert_eq!(comp1.read().unwrap().value, 1);
178        assert_eq!(comp2.read().unwrap().value, 2);
179        assert_eq!(comp3.read().unwrap().value, 3);
180    }
181}