verdure_ioc/
component.rs

1//! Component definition and initialization system
2//!
3//! This module provides the core abstractions for defining and initializing components
4//! in the IoC container. It includes component scopes, dependency management, and
5//! component definition structures.
6
7pub mod factory;
8
9use std::any::{Any, TypeId};
10use std::collections::HashMap;
11use std::sync::Arc;
12use verdure_core::error::component::ComponentError;
13
14/// Type alias for component instances stored in the container
15///
16/// All components are stored as `Arc<dyn Any + Send + Sync>` to enable
17/// type-safe downcasting while maintaining thread safety.
18pub type ComponentInstance = Arc<dyn Any + Send + Sync>;
19
20/// Enumeration of component lifecycle scopes
21///
22/// This enum defines how component instances are managed by the container.
23///
24/// # Variants
25///
26/// * `Singleton` - Only one instance of the component exists throughout the application lifecycle
27/// * `Prototype` - A new instance is created each time the component is requested
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ComponentScope {
30    /// Single shared instance across the entire application
31    Singleton,
32    /// New instance created on each request
33    Prototype,
34}
35
36/// Definition structure for registering components with the container
37///
38/// `ComponentDefinition` contains all the metadata and factory functions needed
39/// to create and manage component instances. This structure is typically generated
40/// by the `#[derive(Component)]` macro but can also be created manually.
41///
42/// # Examples
43///
44/// ```rust
45/// use verdure_ioc::{ComponentDefinition, ComponentScope, ComponentInstance};
46/// use std::collections::HashMap;
47/// use std::sync::Arc;
48/// use std::any::TypeId;
49///
50/// #[derive(Debug)]
51/// struct MyService {
52///     value: i32,
53/// }
54///
55/// let definition = ComponentDefinition {
56///     type_id: || TypeId::of::<MyService>(),
57///     type_name: "MyService",
58///     scope: || ComponentScope::Singleton,
59///     dependencies: || vec![],
60///     creator: |_deps| Ok(Arc::new(MyService { value: 42 })),
61/// };
62/// ```
63#[derive(Debug)]
64pub struct ComponentDefinition {
65    /// Function that returns the TypeId of the component
66    pub type_id: fn() -> TypeId,
67    /// Human-readable name of the component type
68    pub type_name: &'static str,
69    /// Function that returns the component's scope
70    pub scope: fn() -> ComponentScope,
71    /// Function that returns the TypeIds of the component's dependencies
72    pub dependencies: fn() -> Vec<TypeId>,
73    /// Function that creates an instance of the component given its dependencies
74    pub creator:
75        fn(deps: HashMap<TypeId, ComponentInstance>) -> Result<ComponentInstance, ComponentError>,
76}
77
78inventory::collect!(ComponentDefinition);
79
80/// Trait for components that can be automatically initialized by the container
81///
82/// This trait is typically implemented by the `#[derive(Component)]` macro,
83/// but can also be implemented manually for custom component initialization logic.
84///
85/// # Type Parameters
86///
87/// * `Dependencies` - A tuple type representing the component's dependencies
88///
89/// # Examples
90///
91/// ```rust
92/// use verdure_ioc::{ComponentInitializer, ComponentScope};
93/// use std::sync::Arc;
94///
95/// struct DatabaseService {
96///     connection_string: String,
97/// }
98///
99/// struct UserService {
100///     db: Arc<DatabaseService>,
101/// }
102///
103/// impl ComponentInitializer for UserService {
104///     type Dependencies = (Arc<DatabaseService>,);
105///
106///     fn __new(deps: Self::Dependencies) -> Self {
107///         let (db,) = deps;
108///         Self { db }
109///     }
110///
111///     fn __scope() -> ComponentScope {
112///         ComponentScope::Singleton
113///     }
114/// }
115/// ```
116pub trait ComponentInitializer: Sized {
117    /// The type representing this component's dependencies
118    type Dependencies;
119
120    /// Creates a new instance of the component with the provided dependencies
121    ///
122    /// # Arguments
123    ///
124    /// * `deps` - The resolved dependencies for this component
125    fn __new(deps: Self::Dependencies) -> Self;
126
127    /// Returns the scope for this component type
128    fn __scope() -> ComponentScope;
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[derive(Debug, PartialEq)]
136    struct SimpleComponent {
137        value: i32,
138    }
139
140    #[derive(Debug)]
141    struct ComponentWithDependencies {
142        simple: Arc<SimpleComponent>,
143        message: String,
144    }
145
146    impl ComponentInitializer for SimpleComponent {
147        type Dependencies = ();
148
149        fn __new(_deps: Self::Dependencies) -> Self {
150            SimpleComponent { value: 42 }
151        }
152
153        fn __scope() -> ComponentScope {
154            ComponentScope::Singleton
155        }
156    }
157
158    impl ComponentInitializer for ComponentWithDependencies {
159        type Dependencies = (Arc<SimpleComponent>,);
160
161        fn __new(deps: Self::Dependencies) -> Self {
162            let (simple,) = deps;
163            ComponentWithDependencies {
164                simple,
165                message: "Hello".to_string(),
166            }
167        }
168
169        fn __scope() -> ComponentScope {
170            ComponentScope::Prototype
171        }
172    }
173
174    #[test]
175    fn test_component_scope() {
176        match ComponentScope::Singleton {
177            ComponentScope::Singleton => assert!(true),
178            ComponentScope::Prototype => assert!(false),
179        }
180
181        match ComponentScope::Prototype {
182            ComponentScope::Singleton => assert!(false),
183            ComponentScope::Prototype => assert!(true),
184        }
185    }
186
187    #[test]
188    fn test_simple_component_initializer() {
189        let component = SimpleComponent::__new(());
190        assert_eq!(component.value, 42);
191
192        match SimpleComponent::__scope() {
193            ComponentScope::Singleton => assert!(true),
194            ComponentScope::Prototype => assert!(false),
195        }
196    }
197
198    #[test]
199    fn test_component_with_dependencies_initializer() {
200        let simple_component = Arc::new(SimpleComponent { value: 100 });
201        let deps = (simple_component.clone(),);
202
203        let component = ComponentWithDependencies::__new(deps);
204        assert_eq!(component.simple.value, 100);
205        assert_eq!(component.message, "Hello");
206
207        match ComponentWithDependencies::__scope() {
208            ComponentScope::Singleton => assert!(false),
209            ComponentScope::Prototype => assert!(true),
210        }
211    }
212
213    #[test]
214    fn test_component_definition_structure() {
215        let type_id_fn = || std::any::TypeId::of::<SimpleComponent>();
216        let type_name = "SimpleComponent";
217        let scope_fn = || ComponentScope::Singleton;
218        let dependencies_fn = || vec![];
219        let creator_fn = |deps: HashMap<TypeId, ComponentInstance>| {
220            assert!(deps.is_empty());
221            let instance = SimpleComponent::__new(());
222            Ok(Arc::new(instance) as ComponentInstance)
223        };
224
225        let definition = ComponentDefinition {
226            type_id: type_id_fn,
227            type_name,
228            scope: scope_fn,
229            dependencies: dependencies_fn,
230            creator: creator_fn,
231        };
232
233        assert_eq!((definition.type_id)(), TypeId::of::<SimpleComponent>());
234        assert_eq!(definition.type_name, "SimpleComponent");
235        assert!(matches!((definition.scope)(), ComponentScope::Singleton));
236        assert!((definition.dependencies)().is_empty());
237
238        let result = (definition.creator)(HashMap::new());
239        assert!(result.is_ok());
240    }
241
242    #[test]
243    fn test_component_instance_type() {
244        let simple_component = SimpleComponent { value: 123 };
245        let instance: ComponentInstance = Arc::new(simple_component);
246
247        // Test that we can downcast back to the original type
248        let downcasted = instance.downcast::<SimpleComponent>();
249        assert!(downcasted.is_ok());
250        assert_eq!(downcasted.unwrap().value, 123);
251    }
252
253    #[test]
254    fn test_component_definition_with_dependencies() {
255        let type_id_fn = || std::any::TypeId::of::<ComponentWithDependencies>();
256        let type_name = "ComponentWithDependencies";
257        let scope_fn = || ComponentScope::Prototype;
258        let dependencies_fn = || vec![TypeId::of::<SimpleComponent>()];
259        let creator_fn = |deps: HashMap<TypeId, ComponentInstance>| {
260            let simple_dep = deps
261                .get(&TypeId::of::<SimpleComponent>())
262                .ok_or_else(|| {
263                    verdure_core::error::component::ComponentError::DependencyNotFound(
264                        "SimpleComponent".to_string(),
265                    )
266                })?
267                .clone()
268                .downcast::<SimpleComponent>()
269                .map_err(|_| {
270                    verdure_core::error::component::ComponentError::DowncastFailed(
271                        "SimpleComponent".to_string(),
272                    )
273                })?;
274
275            let instance = ComponentWithDependencies::__new((simple_dep,));
276            Ok(Arc::new(instance) as ComponentInstance)
277        };
278
279        let definition = ComponentDefinition {
280            type_id: type_id_fn,
281            type_name,
282            scope: scope_fn,
283            dependencies: dependencies_fn,
284            creator: creator_fn,
285        };
286
287        assert_eq!(
288            (definition.type_id)(),
289            TypeId::of::<ComponentWithDependencies>()
290        );
291        assert_eq!(definition.type_name, "ComponentWithDependencies");
292        assert!(matches!((definition.scope)(), ComponentScope::Prototype));
293        assert_eq!(
294            (definition.dependencies)(),
295            vec![TypeId::of::<SimpleComponent>()]
296        );
297
298        // Test creator with proper dependency
299        let mut deps = HashMap::new();
300        let simple_instance: ComponentInstance = Arc::new(SimpleComponent { value: 999 });
301        deps.insert(TypeId::of::<SimpleComponent>(), simple_instance);
302
303        let result = (definition.creator)(deps);
304        assert!(result.is_ok());
305
306        let component_instance = result.unwrap();
307        let downcasted = component_instance
308            .downcast::<ComponentWithDependencies>()
309            .unwrap();
310        assert_eq!(downcasted.simple.value, 999);
311        assert_eq!(downcasted.message, "Hello");
312    }
313
314    #[test]
315    fn test_component_definition_creator_missing_dependency() {
316        let creator_fn = |deps: HashMap<TypeId, ComponentInstance>| {
317            let _simple_dep = deps.get(&TypeId::of::<SimpleComponent>()).ok_or_else(|| {
318                verdure_core::error::component::ComponentError::DependencyNotFound(
319                    "SimpleComponent".to_string(),
320                )
321            })?;
322            Ok(Arc::new(ComponentWithDependencies {
323                simple: Arc::new(SimpleComponent { value: 0 }),
324                message: "test".to_string(),
325            }) as ComponentInstance)
326        };
327
328        let result = creator_fn(HashMap::new());
329        assert!(result.is_err());
330
331        if let Err(error) = result {
332            assert!(matches!(
333                error,
334                verdure_core::error::component::ComponentError::DependencyNotFound(_)
335            ));
336        }
337    }
338}