verdure_ioc/component/
factory.rs

1//! Component factory trait and implementations
2//!
3//! This module defines the `ComponentFactory` trait which provides methods for
4//! retrieving components from the IoC container with type safety.
5
6use std::any::{Any, TypeId};
7use std::sync::Arc;
8
9/// Trait for retrieving components from a container
10///
11/// `ComponentFactory` provides both type-erased and type-safe methods for
12/// retrieving component instances from the container. This trait is implemented
13/// by `ComponentContainer` to provide a clean API for component retrieval.
14///
15/// # Examples
16///
17/// ```rust
18/// use verdure_ioc::{ComponentFactory, ComponentContainer};
19/// use std::sync::Arc;
20///
21/// #[derive(Debug)]
22/// struct MyService {
23///     value: i32,
24/// }
25///
26/// let container = ComponentContainer::new();
27/// container.register_component(Arc::new(MyService { value: 42 }));
28///
29/// // Type-safe retrieval
30/// let service: Option<Arc<MyService>> = container.get_component();
31/// assert!(service.is_some());
32///
33/// // Type-erased retrieval
34/// let service_any = container.get_component_by_type_id(std::any::TypeId::of::<MyService>());
35/// assert!(service_any.is_some());
36/// ```
37pub trait ComponentFactory {
38    /// Retrieves a component instance by its TypeId
39    ///
40    /// This method returns the component as a type-erased `Arc<dyn Any + Send + Sync>`,
41    /// which can be downcast to the specific type if needed.
42    ///
43    /// # Arguments
44    ///
45    /// * `type_id` - The TypeId of the component to retrieve
46    ///
47    /// # Returns
48    ///
49    /// `Some(Arc<dyn Any + Send + Sync>)` if the component exists, `None` otherwise
50    fn get_component_by_type_id(&self, type_id: TypeId) -> Option<Arc<dyn Any + Send + Sync>>;
51
52    /// Retrieves a component instance with compile-time type safety
53    ///
54    /// This method provides a type-safe way to retrieve components from the container.
55    /// The compiler will ensure that the returned type matches the requested type.
56    ///
57    /// # Type Parameters
58    ///
59    /// * `T` - The type of component to retrieve. Must implement `Any + Send + Sync`
60    ///
61    /// # Returns
62    ///
63    /// `Some(Arc<T>)` if the component exists and can be downcast to type `T`, `None` otherwise
64    ///
65    /// # Examples
66    ///
67    /// ```rust
68    /// use verdure_ioc::{ComponentFactory, ComponentContainer};
69    /// use std::sync::Arc;
70    ///
71    /// #[derive(Debug)]
72    /// struct DatabaseService {
73    ///     url: String,
74    /// }
75    ///
76    /// let container = ComponentContainer::new();
77    /// let db_service = Arc::new(DatabaseService { url: "localhost:5432".to_string() });
78    /// container.register_component(db_service);
79    ///
80    /// // Retrieve with type safety
81    /// let retrieved: Option<Arc<DatabaseService>> = container.get_component();
82    /// match retrieved {
83    ///     Some(service) => println!("Database URL: {}", service.url),
84    ///     None => println!("DatabaseService not found"),
85    /// }
86    /// ```
87    fn get_component<T: Any + Send + Sync>(&self) -> Option<Arc<T>>;
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use std::collections::HashMap;
94
95    #[derive(Debug, PartialEq)]
96    struct TestComponent {
97        value: i32,
98    }
99
100    #[derive(Debug, PartialEq)]
101    struct AnotherComponent {
102        name: String,
103    }
104
105    struct MockComponentFactory {
106        components: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
107    }
108
109    impl MockComponentFactory {
110        fn new() -> Self {
111            Self {
112                components: HashMap::new(),
113            }
114        }
115
116        fn register<T: Any + Send + Sync>(&mut self, component: T) {
117            self.components
118                .insert(TypeId::of::<T>(), Arc::new(component));
119        }
120    }
121
122    impl ComponentFactory for MockComponentFactory {
123        fn get_component_by_type_id(&self, type_id: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
124            self.components.get(&type_id).cloned()
125        }
126
127        fn get_component<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
128            let component_any = self.get_component_by_type_id(TypeId::of::<T>())?;
129            component_any.downcast().ok()
130        }
131    }
132
133    #[test]
134    fn test_component_factory_get_component() {
135        let mut factory = MockComponentFactory::new();
136
137        let test_component = TestComponent { value: 42 };
138        factory.register(test_component);
139
140        let retrieved: Option<Arc<TestComponent>> = factory.get_component();
141        assert!(retrieved.is_some());
142        assert_eq!(retrieved.unwrap().value, 42);
143    }
144
145    #[test]
146    fn test_component_factory_get_component_by_type_id() {
147        let mut factory = MockComponentFactory::new();
148
149        let test_component = TestComponent { value: 100 };
150        factory.register(test_component);
151
152        let type_id = TypeId::of::<TestComponent>();
153        let retrieved = factory.get_component_by_type_id(type_id);
154        assert!(retrieved.is_some());
155
156        let downcasted = retrieved.unwrap().downcast::<TestComponent>();
157        assert!(downcasted.is_ok());
158        assert_eq!(downcasted.unwrap().value, 100);
159    }
160
161    #[test]
162    fn test_component_factory_multiple_components() {
163        let mut factory = MockComponentFactory::new();
164
165        factory.register(TestComponent { value: 123 });
166        factory.register(AnotherComponent {
167            name: "test".to_string(),
168        });
169
170        let test_comp: Option<Arc<TestComponent>> = factory.get_component();
171        let another_comp: Option<Arc<AnotherComponent>> = factory.get_component();
172
173        assert!(test_comp.is_some());
174        assert!(another_comp.is_some());
175
176        assert_eq!(test_comp.unwrap().value, 123);
177        assert_eq!(another_comp.unwrap().name, "test");
178    }
179
180    #[test]
181    fn test_component_factory_nonexistent_component() {
182        let factory = MockComponentFactory::new();
183
184        let result: Option<Arc<TestComponent>> = factory.get_component();
185        assert!(result.is_none());
186
187        let result_by_type_id = factory.get_component_by_type_id(TypeId::of::<TestComponent>());
188        assert!(result_by_type_id.is_none());
189    }
190
191    #[test]
192    fn test_component_factory_downcast_failure() {
193        let mut factory = MockComponentFactory::new();
194        factory.register(TestComponent { value: 42 });
195
196        // Try to get the TestComponent as AnotherComponent (should fail)
197        let type_id = TypeId::of::<TestComponent>();
198        let component_any = factory.get_component_by_type_id(type_id).unwrap();
199
200        let wrong_downcast = component_any.downcast::<AnotherComponent>();
201        assert!(wrong_downcast.is_err());
202    }
203
204    #[test]
205    fn test_component_factory_trait_object() {
206        let mut factory = MockComponentFactory::new();
207        factory.register(TestComponent { value: 999 });
208
209        // Test using the factory through concrete type (trait objects with generics aren't dyn compatible)
210        let component: Option<Arc<TestComponent>> = factory.get_component();
211
212        assert!(component.is_some());
213        assert_eq!(component.unwrap().value, 999);
214
215        // Test the type-erased method which is dyn compatible
216        let type_id = TypeId::of::<TestComponent>();
217        let component_any = factory.get_component_by_type_id(type_id);
218        assert!(component_any.is_some());
219
220        let downcasted = component_any.unwrap().downcast::<TestComponent>().unwrap();
221        assert_eq!(downcasted.value, 999);
222    }
223}