verdure_ioc/
container.rs

1//! IoC container implementation for the Verdure ecosystem
2//!
3//! This module provides the core `ComponentContainer` implementation that serves as the
4//! foundation for dependency injection across the entire Verdure ecosystem. It manages
5//! component lifecycles, resolves dependencies, and provides the runtime infrastructure
6//! that enables Verdure's declarative programming model.
7
8use crate::event::{ContainerLifecycleEvent, LifecycleEventPublisher};
9use crate::{ComponentDefinition, ComponentFactory, ComponentInstance, ComponentScope};
10use dashmap::{DashMap, DashSet};
11use std::any::{Any, TypeId};
12use std::collections::HashMap;
13use std::sync::Arc;
14use std::time::Instant;
15use verdure_core::error::container::ContainerError;
16
17/// Component descriptor for identifying components in the container
18///
19/// `ComponentDescriptor` uniquely identifies components within the container
20/// using both their type and an optional qualifier string.
21///
22/// # Examples
23///
24/// ```rust
25/// use verdure_ioc::ComponentContainer;
26/// use std::any::TypeId;
27///
28/// #[derive(Debug)]
29/// struct DatabaseService;
30///
31/// let container = ComponentContainer::new();
32/// // ComponentDescriptor is used internally by the container
33/// // Users typically don't need to create descriptors manually
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct ComponentDescriptor {
37    /// The TypeId of the component
38    pub type_id: TypeId,
39    /// Optional qualifier for named instances
40    pub qualifier: Option<&'static str>,
41}
42
43impl ComponentDescriptor {
44    /// Creates a new ComponentDescriptor with the given type and optional qualifier
45    ///
46    /// # Arguments
47    ///
48    /// * `type_id` - The TypeId of the component
49    /// * `qualifier` - Optional qualifier string for named instances
50    pub fn new(type_id: TypeId, qualifier: Option<&'static str>) -> Self {
51        Self { type_id, qualifier }
52    }
53
54    /// Creates a ComponentDescriptor for the specified type without a qualifier
55    ///
56    /// # Type Parameters
57    ///
58    /// * `T` - The component type
59    pub fn for_type<T: 'static>() -> Self {
60        Self {
61            type_id: TypeId::of::<T>(),
62            qualifier: None,
63        }
64    }
65
66    /// Creates a ComponentDescriptor for the specified type with a qualifier
67    ///
68    /// # Arguments
69    ///
70    /// * `qualifier` - The qualifier string for named instances
71    ///
72    /// # Type Parameters
73    ///
74    /// * `T` - The component type
75    pub fn with_qualifier<T: 'static>(qualifier: &'static str) -> Self {
76        Self {
77            type_id: TypeId::of::<T>(),
78            qualifier: Some(qualifier),
79        }
80    }
81}
82
83/// Statistics for component creation and access
84///
85/// `ComponentStats` tracks various metrics about component usage for
86/// monitoring and debugging purposes.
87#[derive(Debug, Default, Clone)]
88pub struct ComponentStats {
89    /// When the component was first created
90    pub created_at: Option<Instant>,
91    /// When the component was last accessed
92    pub last_accessed: Option<Instant>,
93    /// Total number of times the component has been accessed
94    pub access_count: u64,
95    /// Time taken to create the component instance (in milliseconds)
96    pub creation_time: u64,
97}
98
99/// The central IoC container for the Verdure ecosystem
100///
101/// `ComponentContainer` serves as the heart of the Verdure ecosystem's dependency injection system.
102/// It provides the runtime infrastructure that enables all Verdure modules - from web frameworks
103/// to data access layers - to work together through declarative component management.
104///
105/// This container powers the entire Verdure ecosystem by providing:
106/// * **Cross-module Integration**: Components from different Verdure modules can seamlessly depend on each other
107/// * **Ecosystem Coherence**: Unified component management across verdure-web, verdure-data, verdure-security, etc.
108///
109/// # Features
110///
111/// * **Thread-safe**: Uses concurrent data structures for safe multi-threaded access
112/// * **Dependency Resolution**: Automatically resolves and injects component dependencies
113/// * **Circular Dependency Detection**: Prevents infinite dependency loops during resolution
114/// * **Lifecycle Events**: Publishes events during container and component lifecycle operations
115/// * **Statistics Tracking**: Maintains creation and access statistics for monitoring
116///
117/// # Examples
118///
119/// ```rust
120/// use verdure_ioc::{ComponentContainer, ComponentFactory};
121/// use std::sync::Arc;
122///
123/// #[derive(Debug)]
124/// struct DatabaseService {
125///     connection_string: String,
126/// }
127///
128/// // Create container
129/// let container = ComponentContainer::new();
130///
131/// // Register a component
132/// let db_service = Arc::new(DatabaseService {
133///     connection_string: "postgres://localhost:5432/mydb".to_string(),
134/// });
135/// container.register_component(db_service);
136///
137/// // Retrieve the component
138/// let retrieved: Option<Arc<DatabaseService>> = container.get_component();
139/// assert!(retrieved.is_some());
140///
141/// // Initialize automatic components (from #[derive(Component)])
142/// let result = container.initialize();
143/// assert!(result.is_ok());
144/// ```
145pub struct ComponentContainer {
146    /// Map of component descriptors to their instances
147    components: DashMap<ComponentDescriptor, ComponentInstance>,
148    /// Set tracking which components are currently being initialized (for circular dependency detection)
149    initializing: DashSet<TypeId>,
150    /// Statistics for each component
151    stats: DashMap<ComponentDescriptor, ComponentStats>,
152    /// Event publisher for lifecycle events
153    lifecycle_publisher: Arc<LifecycleEventPublisher>,
154}
155
156impl ComponentContainer {
157    /// Creates a new empty ComponentContainer
158    ///
159    /// # Examples
160    ///
161    /// ```rust
162    /// use verdure_ioc::ComponentContainer;
163    ///
164    /// let container = ComponentContainer::new();
165    /// ```
166    pub fn new() -> Self {
167        Self {
168            components: DashMap::new(),
169            initializing: DashSet::new(),
170            stats: DashMap::new(),
171            lifecycle_publisher: Arc::new(LifecycleEventPublisher::new()),
172        }
173    }
174
175    /// Initializes the container by discovering and creating all registered components
176    ///
177    /// This method scans for all components registered via the `#[derive(Component)]` macro
178    /// and creates instances of them, resolving their dependencies automatically.
179    /// It also publishes lifecycle events during the initialization process.
180    ///
181    /// # Returns
182    ///
183    /// * `Ok(())` - If initialization completed successfully
184    /// * `Err(ContainerError)` - If there was an error during initialization (e.g., circular dependencies)
185    ///
186    /// # Examples
187    ///
188    /// ```rust
189    /// use verdure_ioc::ComponentContainer;
190    ///
191    /// let container = ComponentContainer::new();
192    /// match container.initialize() {
193    ///     Ok(_) => println!("Container initialized successfully"),
194    ///     Err(e) => eprintln!("Initialization failed: {}", e),
195    /// }
196    /// ```
197    pub fn initialize(&self) -> Result<(), ContainerError> {
198        let component_count = inventory::iter::<ComponentDefinition>().count();
199
200        self.lifecycle_publisher
201            .publish(&ContainerLifecycleEvent::InitializationStarted {
202                container: self,
203                component_count,
204            });
205
206        let start_time = Instant::now();
207
208        let mut def_map = HashMap::new();
209        for def in inventory::iter::<ComponentDefinition> {
210            let type_id = (def.type_id)();
211            def_map.insert(type_id, def);
212        }
213
214        for def in inventory::iter::<ComponentDefinition> {
215            let type_id = (def.type_id)();
216            let descriptor = ComponentDescriptor::new(type_id, None);
217
218            if !self.components.contains_key(&descriptor) {
219                self.resolve_bean(&descriptor, &def_map)?;
220            } else {
221                // TODO: Duplicate registration failed
222            }
223        }
224
225        self.lifecycle_publisher
226            .publish(&ContainerLifecycleEvent::InitializationCompleted {
227                container: self,
228                component_count: self.components.len(),
229                duration: start_time.elapsed(),
230            });
231
232        Ok(())
233    }
234
235    /// Registers a pre-created component instance with the container
236    ///
237    /// This method allows manual registration of component instances that have been
238    /// created outside the container's automatic initialization process.
239    ///
240    /// # Arguments
241    ///
242    /// * `instance` - The component instance to register
243    ///
244    /// # Examples
245    ///
246    /// ```rust
247    /// use verdure_ioc::ComponentContainer;
248    /// use std::sync::Arc;
249    ///
250    /// #[derive(Debug)]
251    /// struct ConfigService {
252    ///     config_path: String,
253    /// }
254    ///
255    /// let container = ComponentContainer::new();
256    /// let config = Arc::new(ConfigService {
257    ///     config_path: "/etc/myapp/config.toml".to_string(),
258    /// });
259    ///
260    /// container.register_component(config);
261    /// ```
262    pub fn register_component(&self, instance: ComponentInstance) {
263        let type_id = (*instance).type_id();
264        self.register_component_by_type_id(type_id, instance);
265    }
266
267    /// Registers a component instance with the container using a specific TypeId
268    ///
269    /// This method is useful when you need to register a component with a different
270    /// type identity than its concrete type.
271    ///
272    /// # Arguments
273    ///
274    /// * `type_id` - The TypeId to use for component registration
275    /// * `instance` - The component instance to register
276    pub fn register_component_by_type_id(&self, type_id: TypeId, instance: ComponentInstance) {
277        let descriptor = ComponentDescriptor::new(type_id, None);
278        self.components.insert(descriptor, instance);
279    }
280
281    fn resolve_bean(
282        &self,
283        descriptor: &ComponentDescriptor,
284        def_map: &HashMap<TypeId, &ComponentDefinition>,
285    ) -> Result<ComponentInstance, ContainerError> {
286        if !self.initializing.insert(descriptor.type_id) {
287            let type_name = def_map
288                .get(&descriptor.type_id)
289                .map_or("Unknown", |d| d.type_name);
290            return Err(ContainerError::circular_dependency(format!(
291                "{}",
292                type_name
293            )));
294        }
295
296        let def = match def_map.get(&descriptor.type_id) {
297            Some(d) => *d,
298            None => {
299                self.initializing.remove(&descriptor.type_id);
300                return Err(ContainerError::not_found(format!(
301                    "Bean definition not found for type ID {:?}",
302                    descriptor.type_id
303                )));
304            }
305        };
306
307        let dependencies = (def.dependencies)();
308        let mut deps_map = HashMap::new();
309        for dep_id in dependencies {
310            let dep_descriptor = ComponentDescriptor::new(dep_id, None);
311
312            // exist in components
313            if let Some(instance) = self.components.get(&dep_descriptor) {
314                deps_map.insert(dep_id, instance.clone());
315                continue;
316            }
317
318            if let Some(_dep_def) = def_map.get(&dep_id) {
319                let dep_instance = self.resolve_bean(&dep_descriptor, def_map)?;
320                deps_map.insert(dep_id, dep_instance);
321            } else {
322                self.initializing.remove(&descriptor.type_id);
323                return Err(ContainerError::not_found(format!(
324                    "Dependency not found for type ID {:?}",
325                    dep_id
326                )));
327            }
328        }
329
330        let start = Instant::now();
331        let instance = match (def.creator)(deps_map) {
332            Ok(i) => i,
333            Err(e) => {
334                self.initializing.remove(&descriptor.type_id);
335                return Err(ContainerError::creation_failed(format!(
336                    "Failed to create bean '{}': '{}'",
337                    def.type_name, e
338                )));
339            }
340        };
341        let creation_time = start.elapsed();
342
343        self.lifecycle_publisher
344            .publish(&ContainerLifecycleEvent::ComponentCreated {
345                container: self,
346                component_name: def.type_name,
347                component_type_id: descriptor.type_id,
348                creation_duration: creation_time,
349            });
350
351        self.initializing.remove(&descriptor.type_id);
352
353        match (def.scope)() {
354            ComponentScope::Singleton => {
355                self.components.insert(descriptor.clone(), instance.clone());
356            }
357            _ => {}
358        };
359
360        self.stats.insert(
361            descriptor.clone(),
362            ComponentStats {
363                created_at: Some(Instant::now()),
364                last_accessed: Some(Instant::now()),
365                access_count: 1,
366                creation_time: creation_time.as_millis() as u64,
367            },
368        );
369
370        Ok(instance)
371    }
372}
373
374impl ComponentFactory for ComponentContainer {
375    fn get_component_by_type_id(&self, type_id: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
376        Some(
377            self.components
378                .get(&ComponentDescriptor::new(type_id, None))?
379                .clone(),
380        )
381    }
382
383    fn get_component<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
384        let component_any = self.get_component_by_type_id(TypeId::of::<T>())?;
385        component_any.downcast().ok()
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392    use crate::ComponentInitializer;
393    use std::sync::atomic::{AtomicU32, Ordering};
394
395    #[derive(Debug)]
396    struct TestComponent {
397        value: u32,
398    }
399
400    impl TestComponent {
401        fn new(value: u32) -> Self {
402            Self { value }
403        }
404    }
405
406    #[derive(Debug)]
407    struct TestComponentWithDeps {
408        dependency: Arc<TestComponent>,
409        value: String,
410    }
411
412    static CREATION_COUNTER: AtomicU32 = AtomicU32::new(0);
413
414    impl ComponentInitializer for TestComponent {
415        type Dependencies = ();
416
417        fn __new(_deps: Self::Dependencies) -> Self {
418            CREATION_COUNTER.fetch_add(1, Ordering::SeqCst);
419            TestComponent::new(42)
420        }
421
422        fn __scope() -> crate::ComponentScope {
423            crate::ComponentScope::Singleton
424        }
425    }
426
427    impl ComponentInitializer for TestComponentWithDeps {
428        type Dependencies = (Arc<TestComponent>,);
429
430        fn __new(deps: Self::Dependencies) -> Self {
431            let (dependency,) = deps;
432            TestComponentWithDeps {
433                dependency,
434                value: "test".to_string(),
435            }
436        }
437
438        fn __scope() -> crate::ComponentScope {
439            crate::ComponentScope::Singleton
440        }
441    }
442
443    #[test]
444    fn test_component_descriptor() {
445        let desc1 = ComponentDescriptor::for_type::<TestComponent>();
446        let desc2 = ComponentDescriptor::new(TypeId::of::<TestComponent>(), None);
447        assert_eq!(desc1, desc2);
448
449        let desc_with_qualifier = ComponentDescriptor::with_qualifier::<TestComponent>("test");
450        assert_ne!(desc1, desc_with_qualifier);
451        assert_eq!(desc_with_qualifier.qualifier, Some("test"));
452    }
453
454    #[test]
455    fn test_container_creation() {
456        let container = ComponentContainer::new();
457        assert!(container.components.is_empty());
458        assert!(container.initializing.is_empty());
459        assert!(container.stats.is_empty());
460    }
461
462    #[test]
463    fn test_manual_component_registration() {
464        let container = ComponentContainer::new();
465        let test_component = Arc::new(TestComponent::new(100));
466
467        container.register_component(test_component.clone());
468
469        let retrieved: Option<Arc<TestComponent>> = container.get_component();
470        assert!(retrieved.is_some());
471        assert_eq!(retrieved.unwrap().value, 100);
472    }
473
474    #[test]
475    fn test_register_component_by_type_id() {
476        let container = ComponentContainer::new();
477        let test_component = Arc::new(TestComponent::new(200));
478        let type_id = TypeId::of::<TestComponent>();
479
480        container.register_component_by_type_id(type_id, test_component);
481
482        let retrieved = container.get_component_by_type_id(type_id);
483        assert!(retrieved.is_some());
484
485        let downcast_component: Result<Arc<TestComponent>, _> = retrieved.unwrap().downcast();
486        assert!(downcast_component.is_ok());
487        assert_eq!(downcast_component.unwrap().value, 200);
488    }
489
490    #[test]
491    fn test_get_nonexistent_component() {
492        let container = ComponentContainer::new();
493        let result: Option<Arc<TestComponent>> = container.get_component();
494        assert!(result.is_none());
495    }
496
497    #[test]
498    fn test_component_stats_default() {
499        let stats = ComponentStats::default();
500        assert!(stats.created_at.is_none());
501        assert!(stats.last_accessed.is_none());
502        assert_eq!(stats.access_count, 0);
503        assert_eq!(stats.creation_time, 0);
504    }
505
506    #[test]
507    fn test_component_descriptor_hash_and_eq() {
508        let desc1 = ComponentDescriptor::for_type::<TestComponent>();
509        let desc2 = ComponentDescriptor::for_type::<TestComponent>();
510        let desc3 = ComponentDescriptor::with_qualifier::<TestComponent>("test");
511
512        assert_eq!(desc1, desc2);
513        assert_ne!(desc1, desc3);
514
515        // Test hash by inserting into HashMap
516        let mut map = std::collections::HashMap::new();
517        map.insert(desc1.clone(), "value1");
518        map.insert(desc3.clone(), "value2");
519
520        assert_eq!(map.get(&desc2), Some(&"value1"));
521        assert_eq!(map.get(&desc3), Some(&"value2"));
522    }
523}