verdure_ioc/
event.rs

1//! Container lifecycle event system for the Verdure ecosystem
2//!
3//! This module provides a comprehensive event system for monitoring container
4//! and component lifecycle events across the Verdure ecosystem. It enables
5//! observability and debugging of the dependency injection process that powers
6//! all Verdure applications, from simple services to complex web applications.
7
8use crate::container::ComponentContainer;
9use std::any::TypeId;
10use std::time::Duration;
11
12/// Container lifecycle events enumeration
13///
14/// This enum represents different events that occur during the container's lifecycle,
15/// including initialization phases and component creation events.
16///
17/// # Examples
18///
19/// ```rust
20/// use verdure_ioc::{ContainerLifecycleEvent, ComponentContainer};
21///
22/// fn handle_event(event: &ContainerLifecycleEvent) {
23///     match event {
24///         ContainerLifecycleEvent::InitializationStarted { component_count, .. } => {
25///             println!("Starting initialization of {} components", component_count);
26///         }
27///         ContainerLifecycleEvent::InitializationCompleted { duration, .. } => {
28///             println!("Initialization completed in {:?}", duration);
29///         }
30///         ContainerLifecycleEvent::ComponentCreated { component_name, .. } => {
31///             println!("Created component: {}", component_name);
32///         }
33///     }
34/// }
35/// ```
36pub enum ContainerLifecycleEvent<'a> {
37    /// Fired when container initialization begins
38    InitializationStarted {
39        /// Reference to the container being initialized
40        container: &'a ComponentContainer,
41        /// Total number of components to be initialized
42        component_count: usize,
43    },
44    /// Fired when container initialization completes successfully
45    InitializationCompleted {
46        /// Reference to the initialized container
47        container: &'a ComponentContainer,
48        /// Number of components that were successfully initialized
49        component_count: usize,
50        /// Total time taken for initialization
51        duration: Duration,
52    },
53    /// Fired when an individual component is created
54    ComponentCreated {
55        /// Reference to the container
56        container: &'a ComponentContainer,
57        /// Human-readable name of the component type
58        component_name: &'static str,
59        /// TypeId of the created component
60        component_type_id: TypeId,
61        /// Time taken to create this specific component
62        creation_duration: Duration,
63    },
64}
65
66/// Trait for implementing lifecycle event listeners
67///
68/// Implement this trait to receive notifications about container lifecycle events.
69/// Listeners must be thread-safe as they may be called from multiple threads.
70///
71/// # Examples
72///
73/// ```rust
74/// use verdure_ioc::{LifecycleListener, ContainerLifecycleEvent};
75///
76/// struct MyListener;
77///
78/// impl LifecycleListener for MyListener {
79///     fn on_lifecycle_event(&self, event: &ContainerLifecycleEvent) {
80///         println!("Received lifecycle event");
81///     }
82/// }
83/// ```
84pub trait LifecycleListener: Send + Sync {
85    /// Called when a lifecycle event occurs
86    ///
87    /// # Arguments
88    ///
89    /// * `event` - The lifecycle event that occurred
90    fn on_lifecycle_event(&self, event: &ContainerLifecycleEvent);
91}
92
93/// Static definition of a lifecycle event listener
94///
95/// This structure is used to register event listeners with the container
96/// using the `lifecycle_listener!` macro. It contains the listener's name
97/// and handler function.
98///
99/// # Examples
100///
101/// ```rust
102/// use verdure_ioc::{LifecycleListenerDefinition, ContainerLifecycleEvent};
103///
104/// fn my_handler(event: &ContainerLifecycleEvent) {
105///     println!("Event received");
106/// }
107///
108/// let definition = LifecycleListenerDefinition {
109///     name: "my_listener",
110///     handler: my_handler,
111/// };
112/// ```
113pub struct LifecycleListenerDefinition {
114    /// Unique name identifying this listener
115    pub name: &'static str,
116    /// Function to call when events occur
117    pub handler: fn(&ContainerLifecycleEvent),
118}
119
120inventory::collect!(LifecycleListenerDefinition);
121
122/// Publisher for container lifecycle events
123///
124/// `LifecycleEventPublisher` manages the collection of registered event listeners
125/// and dispatches events to all registered handlers. It is used internally by
126/// the container to publish events during various lifecycle phases.
127///
128/// # Thread Safety
129///
130/// This struct is thread-safe and can be shared across multiple threads.
131/// Event publishing is synchronous and will call all listeners in sequence.
132pub struct LifecycleEventPublisher {
133    /// Collection of all registered listener definitions
134    listeners: Vec<&'static LifecycleListenerDefinition>,
135}
136
137impl LifecycleEventPublisher {
138    /// Creates a new LifecycleEventPublisher
139    ///
140    /// This method automatically discovers all registered lifecycle listeners
141    /// using the inventory system and prepares them for event dispatching.
142    ///
143    /// # Examples
144    ///
145    /// ```rust
146    /// use verdure_ioc::LifecycleEventPublisher;
147    ///
148    /// let publisher = LifecycleEventPublisher::new();
149    /// ```
150    pub fn new() -> Self {
151        let listeners: Vec<&'static LifecycleListenerDefinition> =
152            inventory::iter::<LifecycleListenerDefinition>().collect();
153
154        Self { listeners }
155    }
156
157    /// Publishes an event to all registered listeners
158    ///
159    /// This method synchronously calls all registered event handlers with the provided event.
160    /// If any listener panics, the panic will propagate up to the caller.
161    ///
162    /// # Arguments
163    ///
164    /// * `event` - The lifecycle event to publish
165    ///
166    /// # Examples
167    ///
168    /// ```rust
169    /// use verdure_ioc::{LifecycleEventPublisher, ContainerLifecycleEvent, ComponentContainer};
170    ///
171    /// let publisher = LifecycleEventPublisher::new();
172    /// let container = ComponentContainer::new();
173    ///
174    /// let event = ContainerLifecycleEvent::InitializationStarted {
175    ///     container: &container,
176    ///     component_count: 0,
177    /// };
178    ///
179    /// publisher.publish(&event);
180    /// ```
181    pub fn publish(&self, event: &ContainerLifecycleEvent) {
182        for listener in &self.listeners {
183            (listener.handler)(event);
184        }
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use crate::ComponentContainer;
192    use std::sync::Arc;
193    use std::sync::atomic::{AtomicUsize, Ordering};
194    use std::time::Duration;
195
196    static EVENT_COUNTER: AtomicUsize = AtomicUsize::new(0);
197    static LAST_EVENT_TYPE: AtomicUsize = AtomicUsize::new(0);
198
199    fn test_event_handler(event: &ContainerLifecycleEvent) {
200        EVENT_COUNTER.fetch_add(1, Ordering::SeqCst);
201
202        match event {
203            ContainerLifecycleEvent::InitializationStarted { .. } => {
204                LAST_EVENT_TYPE.store(1, Ordering::SeqCst);
205            }
206            ContainerLifecycleEvent::InitializationCompleted { .. } => {
207                LAST_EVENT_TYPE.store(2, Ordering::SeqCst);
208            }
209            ContainerLifecycleEvent::ComponentCreated { .. } => {
210                LAST_EVENT_TYPE.store(3, Ordering::SeqCst);
211            }
212        }
213    }
214
215    #[test]
216    fn test_lifecycle_listener_definition() {
217        let definition = LifecycleListenerDefinition {
218            name: "test_listener",
219            handler: test_event_handler,
220        };
221
222        assert_eq!(definition.name, "test_listener");
223
224        // Reset counter
225        EVENT_COUNTER.store(0, Ordering::SeqCst);
226
227        let container = ComponentContainer::new();
228        let event = ContainerLifecycleEvent::InitializationStarted {
229            container: &container,
230            component_count: 5,
231        };
232
233        (definition.handler)(&event);
234        assert_eq!(EVENT_COUNTER.load(Ordering::SeqCst), 1);
235        assert_eq!(LAST_EVENT_TYPE.load(Ordering::SeqCst), 1);
236    }
237
238    #[test]
239    fn test_lifecycle_event_publisher_creation() {
240        let publisher = LifecycleEventPublisher::new();
241        // The listeners vec will be populated from inventory,
242        // but since we're in a test environment without registered listeners,
243        // it should be empty or contain only test listeners
244        assert!(publisher.listeners.len() >= 0);
245    }
246
247    #[test]
248    fn test_container_lifecycle_events() {
249        let container = ComponentContainer::new();
250
251        // Test InitializationStarted event
252        let init_started = ContainerLifecycleEvent::InitializationStarted {
253            container: &container,
254            component_count: 10,
255        };
256
257        match &init_started {
258            ContainerLifecycleEvent::InitializationStarted {
259                component_count, ..
260            } => {
261                assert_eq!(*component_count, 10);
262            }
263            _ => panic!("Expected InitializationStarted event"),
264        }
265
266        // Test InitializationCompleted event
267        let init_completed = ContainerLifecycleEvent::InitializationCompleted {
268            container: &container,
269            component_count: 10,
270            duration: Duration::from_millis(100),
271        };
272
273        match &init_completed {
274            ContainerLifecycleEvent::InitializationCompleted {
275                component_count,
276                duration,
277                ..
278            } => {
279                assert_eq!(*component_count, 10);
280                assert_eq!(duration.as_millis(), 100);
281            }
282            _ => panic!("Expected InitializationCompleted event"),
283        }
284
285        // Test ComponentCreated event
286        let component_created = ContainerLifecycleEvent::ComponentCreated {
287            container: &container,
288            component_name: "TestComponent",
289            component_type_id: std::any::TypeId::of::<i32>(),
290            creation_duration: Duration::from_millis(50),
291        };
292
293        match &component_created {
294            ContainerLifecycleEvent::ComponentCreated {
295                component_name,
296                component_type_id,
297                creation_duration,
298                ..
299            } => {
300                assert_eq!(*component_name, "TestComponent");
301                assert_eq!(*component_type_id, std::any::TypeId::of::<i32>());
302                assert_eq!(creation_duration.as_millis(), 50);
303            }
304            _ => panic!("Expected ComponentCreated event"),
305        }
306    }
307
308    struct MockLifecycleListener {
309        name: String,
310        events_received: Arc<AtomicUsize>,
311    }
312
313    impl MockLifecycleListener {
314        fn new(name: &str) -> Self {
315            Self {
316                name: name.to_string(),
317                events_received: Arc::new(AtomicUsize::new(0)),
318            }
319        }
320
321        fn handle_event(&self, _event: &ContainerLifecycleEvent) {
322            self.events_received.fetch_add(1, Ordering::SeqCst);
323        }
324
325        fn get_event_count(&self) -> usize {
326            self.events_received.load(Ordering::SeqCst)
327        }
328    }
329
330    impl LifecycleListener for MockLifecycleListener {
331        fn on_lifecycle_event(&self, event: &ContainerLifecycleEvent) {
332            self.handle_event(event);
333        }
334    }
335
336    #[test]
337    fn test_lifecycle_listener_trait() {
338        let listener = MockLifecycleListener::new("mock_listener");
339        assert_eq!(listener.get_event_count(), 0);
340
341        let container = ComponentContainer::new();
342        let event = ContainerLifecycleEvent::InitializationStarted {
343            container: &container,
344            component_count: 3,
345        };
346
347        listener.on_lifecycle_event(&event);
348        assert_eq!(listener.get_event_count(), 1);
349
350        let event2 = ContainerLifecycleEvent::InitializationCompleted {
351            container: &container,
352            component_count: 3,
353            duration: Duration::from_millis(200),
354        };
355
356        listener.on_lifecycle_event(&event2);
357        assert_eq!(listener.get_event_count(), 2);
358    }
359
360    #[test]
361    fn test_event_publisher_with_custom_listeners() {
362        // Create a publisher and test publishing events
363        let publisher = LifecycleEventPublisher {
364            listeners: vec![], // Empty for this test since we can't easily inject listeners
365        };
366
367        let container = ComponentContainer::new();
368        let event = ContainerLifecycleEvent::InitializationStarted {
369            container: &container,
370            component_count: 1,
371        };
372
373        // This should not panic even with no listeners
374        publisher.publish(&event);
375    }
376
377    #[test]
378    fn test_event_types_pattern_matching() {
379        let container = ComponentContainer::new();
380
381        let events = vec![
382            ContainerLifecycleEvent::InitializationStarted {
383                container: &container,
384                component_count: 1,
385            },
386            ContainerLifecycleEvent::InitializationCompleted {
387                container: &container,
388                component_count: 1,
389                duration: Duration::from_millis(10),
390            },
391            ContainerLifecycleEvent::ComponentCreated {
392                container: &container,
393                component_name: "Test",
394                component_type_id: std::any::TypeId::of::<String>(),
395                creation_duration: Duration::from_millis(5),
396            },
397        ];
398
399        let mut event_types = Vec::new();
400
401        for event in events {
402            match event {
403                ContainerLifecycleEvent::InitializationStarted { .. } => {
404                    event_types.push("started");
405                }
406                ContainerLifecycleEvent::InitializationCompleted { .. } => {
407                    event_types.push("completed");
408                }
409                ContainerLifecycleEvent::ComponentCreated { .. } => {
410                    event_types.push("created");
411                }
412            }
413        }
414
415        assert_eq!(event_types, vec!["started", "completed", "created"]);
416    }
417}