Skip to main content

verdure_context/
event.rs

1//! Event broadcasting system
2//!
3//! This module provides a comprehensive event broadcasting system for the Verdure context.
4//! It supports application-wide event publishing and subscription, enabling decoupled
5//! communication between different parts of the application.
6
7use dashmap::DashMap;
8use std::any::{Any, TypeId};
9use std::collections::HashMap;
10use std::sync::Arc;
11
12/// Built-in context lifecycle events
13
14/// Event fired when the application context starts initialization
15///
16/// This event is published at the very beginning of the context initialization
17/// process, before any actual initialization work begins.
18#[derive(Debug, Clone)]
19pub struct ContextInitializingEvent {
20    /// Number of configuration sources to be loaded
21    pub config_sources_count: usize,
22    /// Initialization start timestamp
23    pub timestamp: std::time::SystemTime,
24}
25
26impl Event for ContextInitializingEvent {
27    fn name(&self) -> &'static str {
28        "ContextInitializing"
29    }
30
31    fn as_any(&self) -> &dyn Any {
32        self
33    }
34
35    fn into_any(self: Box<Self>) -> Box<dyn Any> {
36        self
37    }
38}
39
40/// Event fired when the application context is initialized
41///
42/// This event is published after the context has been fully initialized,
43/// including all configuration sources and the IoC container.
44#[derive(Debug, Clone)]
45pub struct ContextInitializedEvent {
46    /// Number of configuration sources loaded
47    pub config_sources_count: usize,
48    /// Initialization timestamp
49    pub timestamp: std::time::SystemTime,
50}
51
52impl Event for ContextInitializedEvent {
53    fn name(&self) -> &'static str {
54        "ContextInitialized"
55    }
56
57    fn as_any(&self) -> &dyn Any {
58        self
59    }
60
61    fn into_any(self: Box<Self>) -> Box<dyn Any> {
62        self
63    }
64}
65
66/// Event fired when configuration is changed at runtime
67///
68/// This event is published when configuration values are updated after
69/// the initial context setup.
70#[derive(Debug, Clone)]
71pub struct ConfigurationChangedEvent {
72    /// The configuration key that was changed
73    pub key: String,
74    /// The old value (if any)
75    pub old_value: Option<String>,
76    /// The new value
77    pub new_value: String,
78    /// Change timestamp
79    pub timestamp: std::time::SystemTime,
80}
81
82impl Event for ConfigurationChangedEvent {
83    fn name(&self) -> &'static str {
84        "ConfigurationChanged"
85    }
86
87    fn as_any(&self) -> &dyn Any {
88        self
89    }
90
91    fn into_any(self: Box<Self>) -> Box<dyn Any> {
92        self
93    }
94}
95
96/// Event trait that all events must implement
97///
98/// This trait allows events to be stored and transmitted in a type-safe manner
99/// while supporting downcasting to the concrete event type.
100///
101/// # Examples
102///
103/// ```rust
104/// use verdure_context::Event;
105/// use std::any::Any;
106///
107/// #[derive(Debug, Clone)]
108/// struct UserCreatedEvent {
109///     pub user_id: u64,
110///     pub username: String,
111/// }
112///
113/// impl Event for UserCreatedEvent {
114///     fn name(&self) -> &'static str {
115///         "UserCreated"
116///     }
117///     
118///     fn as_any(&self) -> &dyn Any {
119///         self
120///     }
121///     
122///     fn into_any(self: Box<Self>) -> Box<dyn Any> {
123///         self
124///     }
125/// }
126/// ```
127pub trait Event: Any + Send + Sync {
128    /// Returns the name of the event type
129    ///
130    /// This should be a unique identifier for the event type.
131    fn name(&self) -> &'static str;
132
133    /// Returns the event as `Any` for downcasting
134    fn as_any(&self) -> &dyn Any;
135
136    /// Converts the event to `Any` for downcasting (consuming)
137    fn into_any(self: Box<Self>) -> Box<dyn Any>;
138}
139
140/// Event listener trait
141///
142/// Implement this trait to handle specific event types. The listener will be
143/// called whenever an event of the specified type is published.
144///
145/// # Examples
146///
147/// ```rust
148/// use verdure_context::{Event, EventListener};
149/// use std::any::Any;
150///
151/// #[derive(Debug, Clone)]
152/// struct MyEvent {
153///     data: String,
154/// }
155///
156/// impl Event for MyEvent {
157///     fn name(&self) -> &'static str {
158///         "MyEvent"
159///     }
160///     
161///     fn as_any(&self) -> &dyn Any {
162///         self
163///     }
164///     
165///     fn into_any(self: Box<Self>) -> Box<dyn Any> {
166///         self
167///     }
168/// }
169///
170/// struct MyEventListener;
171///
172/// impl EventListener<MyEvent> for MyEventListener {
173///     fn on_event(&self, event: &MyEvent) {
174///         println!("Received event with data: {}", event.data);
175///     }
176/// }
177/// ```
178pub trait EventListener<T: Event>: Send + Sync {
179    /// Called when an event of type `T` is published
180    ///
181    /// # Arguments
182    ///
183    /// * `event` - The event instance
184    fn on_event(&self, event: &T);
185}
186
187/// Type-erased event listener
188///
189/// This allows storing listeners of different event types in the same collection.
190pub trait AnyEventListener: Send + Sync {
191    /// Handles an event if the listener is registered for this event type
192    ///
193    /// # Arguments
194    ///
195    /// * `event` - The event to handle
196    ///
197    /// # Returns
198    ///
199    /// `true` if the listener handled the event, `false` otherwise
200    fn handle_event(&self, event: &dyn Event) -> bool;
201
202    /// Returns the TypeId of the event type this listener handles
203    fn event_type_id(&self) -> TypeId;
204}
205
206/// Context-aware event listener trait for lifecycle events
207///
208/// This trait allows event listeners to receive a reference to the ApplicationContext
209/// along with the event, enabling them to interact with the context during lifecycle events.
210///
211/// # Examples
212///
213/// ```rust
214/// use verdure_context::{ContextAwareEventListener, ContextInitializedEvent, ApplicationContext};
215///
216/// struct StartupListener;
217///
218/// impl ContextAwareEventListener<ContextInitializedEvent> for StartupListener {
219///     fn on_context_event(&self, event: &ContextInitializedEvent, context: &ApplicationContext) {
220///         println!("Context initialized with {} sources", event.config_sources_count);
221///         println!("App name: {}", context.get_config("app.name"));
222///         
223///         // Can access IoC container
224///         let container = context.container();
225///         // Can access configuration
226///         let environment = context.environment();
227///         
228///         println!("Running in environment: {}", environment);
229///     }
230/// }
231/// ```
232pub trait ContextAwareEventListener<T: Event>: Send + Sync {
233    /// Called when an event of type `T` is published, with access to the ApplicationContext
234    ///
235    /// # Arguments
236    ///
237    /// * `event` - The event instance
238    /// * `context` - Reference to the ApplicationContext
239    fn on_context_event(&self, event: &T, context: &crate::context::ApplicationContext);
240}
241
242/// Type-erased context-aware event listener
243///
244/// This allows storing context-aware listeners of different event types in the same collection.
245///
246/// This allows storing listeners of different event types in the same collection.
247/// Type-erased context-aware event listener
248///
249/// This allows storing context-aware listeners of different event types in the same collection.
250pub trait AnyContextAwareEventListener: Send + Sync {
251    /// Handles an event with context access if the listener is registered for this event type
252    ///
253    /// # Arguments
254    ///
255    /// * `event` - The event to handle
256    /// * `context` - Reference to the ApplicationContext
257    ///
258    /// # Returns
259    ///
260    /// `true` if the listener handled the event, `false` otherwise
261    fn handle_context_event(
262        &self,
263        event: &dyn Event,
264        context: &crate::context::ApplicationContext,
265    ) -> bool;
266
267    /// Returns the TypeId of the event type this listener handles
268    fn event_type_id(&self) -> TypeId;
269}
270
271/// Implementation of `AnyContextAwareEventListener` for specific context-aware event listeners
272struct TypedContextAwareEventListener<T: Event, L: ContextAwareEventListener<T>> {
273    listener: L,
274    _phantom: std::marker::PhantomData<T>,
275}
276
277impl<T: Event, L: ContextAwareEventListener<T>> TypedContextAwareEventListener<T, L> {
278    fn new(listener: L) -> Self {
279        Self {
280            listener,
281            _phantom: std::marker::PhantomData,
282        }
283    }
284}
285
286impl<T: Event + 'static, L: ContextAwareEventListener<T>> AnyContextAwareEventListener
287    for TypedContextAwareEventListener<T, L>
288{
289    fn handle_context_event(
290        &self,
291        event: &dyn Event,
292        context: &crate::context::ApplicationContext,
293    ) -> bool {
294        if let Some(typed_event) = event.as_any().downcast_ref::<T>() {
295            self.listener.on_context_event(typed_event, context);
296            true
297        } else {
298            false
299        }
300    }
301
302    fn event_type_id(&self) -> TypeId {
303        TypeId::of::<T>()
304    }
305}
306
307/// Implementation of `AnyEventListener` for specific event listeners
308struct TypedEventListener<T: Event, L: EventListener<T>> {
309    listener: L,
310    _phantom: std::marker::PhantomData<T>,
311}
312
313impl<T: Event + 'static, L: EventListener<T>> TypedEventListener<T, L> {
314    fn new(listener: L) -> Self {
315        Self {
316            listener,
317            _phantom: std::marker::PhantomData,
318        }
319    }
320}
321
322impl<T: Event + 'static, L: EventListener<T>> AnyEventListener for TypedEventListener<T, L> {
323    fn handle_event(&self, event: &dyn Event) -> bool {
324        if let Some(typed_event) = event.as_any().downcast_ref::<T>() {
325            self.listener.on_event(typed_event);
326            true
327        } else {
328            false
329        }
330    }
331
332    fn event_type_id(&self) -> TypeId {
333        TypeId::of::<T>()
334    }
335}
336
337/// Event publisher for broadcasting events
338///
339/// `EventPublisher` manages event listeners and provides functionality to publish
340/// events to all registered listeners. It supports type-safe event handling and
341/// allows multiple listeners for the same event type.
342///
343/// # Examples
344///
345/// ```rust
346/// use verdure_context::{EventPublisher, Event, EventListener};
347/// use std::any::Any;
348///
349/// #[derive(Debug, Clone)]
350/// struct TestEvent {
351///     message: String,
352/// }
353///
354/// impl Event for TestEvent {
355///     fn name(&self) -> &'static str {
356///         "TestEvent"
357///     }
358///     
359///     fn as_any(&self) -> &dyn Any {
360///         self
361///     }
362///     
363///     fn into_any(self: Box<Self>) -> Box<dyn Any> {
364///         self
365///     }
366/// }
367///
368/// struct TestListener;
369///
370/// impl EventListener<TestEvent> for TestListener {
371///     fn on_event(&self, event: &TestEvent) {
372///         println!("Received: {}", event.message);
373///     }
374/// }
375///
376/// let mut publisher = EventPublisher::new();
377/// publisher.subscribe(TestListener);
378///
379/// let event = TestEvent {
380///     message: "Hello, World!".to_string(),
381/// };
382/// publisher.publish(&event);
383/// ```
384pub struct EventPublisher {
385    /// Event listeners organized by event type
386    listeners: DashMap<TypeId, Vec<Arc<dyn AnyEventListener>>>,
387    /// Context-aware event listeners organized by event type
388    context_aware_listeners: DashMap<TypeId, Vec<Arc<dyn AnyContextAwareEventListener>>>,
389}
390
391impl EventPublisher {
392    /// Creates a new event publisher
393    ///
394    /// # Examples
395    ///
396    /// ```rust
397    /// use verdure_context::EventPublisher;
398    ///
399    /// let publisher = EventPublisher::new();
400    /// ```
401    pub fn new() -> Self {
402        Self {
403            listeners: DashMap::new(),
404            context_aware_listeners: DashMap::new(),
405        }
406    }
407
408    /// Subscribes a context-aware listener to events of type `T`
409    ///
410    /// Context-aware listeners receive both the event and a reference to the ApplicationContext,
411    /// allowing them to interact with the context during event handling.
412    ///
413    /// # Arguments
414    ///
415    /// * `listener` - The context-aware event listener to register
416    ///
417    /// # Examples
418    ///
419    /// ```rust
420    /// use verdure_context::{EventPublisher, ContextInitializedEvent, ContextAwareEventListener, ApplicationContext};
421    ///
422    /// struct StartupListener;
423    ///
424    /// impl ContextAwareEventListener<ContextInitializedEvent> for StartupListener {
425    ///     fn on_context_event(&self, event: &ContextInitializedEvent, context: &ApplicationContext) {
426    ///         println!("Context initialized with {} sources", event.config_sources_count);
427    ///         println!("App name: {}", context.get_config("app.name"));
428    ///     }
429    /// }
430    ///
431    /// let mut publisher = EventPublisher::new();
432    /// publisher.subscribe_context_aware(StartupListener);
433    /// ```
434    pub fn subscribe_context_aware<
435        T: Event + 'static,
436        L: ContextAwareEventListener<T> + 'static,
437    >(
438        &self,
439        listener: L,
440    ) {
441        let type_id = TypeId::of::<T>();
442        let typed_listener = Arc::new(TypedContextAwareEventListener::new(listener));
443
444        self.context_aware_listeners
445            .entry(type_id)
446            .or_insert_with(Vec::new)
447            .push(typed_listener);
448    }
449    ///
450    /// # Arguments
451    ///
452    /// * `listener` - The event listener to register
453    ///
454    /// # Examples
455    ///
456    /// ```rust
457    /// use verdure_context::{EventPublisher, Event, EventListener};
458    /// use std::any::Any;
459    ///
460    /// #[derive(Debug, Clone)]
461    /// struct MyEvent;
462    ///
463    /// impl Event for MyEvent {
464    ///     fn name(&self) -> &'static str { "MyEvent" }
465    ///     fn as_any(&self) -> &dyn Any { self }
466    ///     fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
467    /// }
468    ///
469    /// struct MyListener;
470    ///
471    /// impl EventListener<MyEvent> for MyListener {
472    ///     fn on_event(&self, _event: &MyEvent) {
473    ///         println!("Event received!");
474    ///     }
475    /// }
476    ///
477    /// let mut publisher = EventPublisher::new();
478    /// publisher.subscribe(MyListener);
479    /// ```
480    pub fn subscribe<T: Event + 'static, L: EventListener<T> + 'static>(&self, listener: L) {
481        let type_id = TypeId::of::<T>();
482        let typed_listener = Arc::new(TypedEventListener::new(listener));
483
484        self.listeners
485            .entry(type_id)
486            .or_insert_with(Vec::new)
487            .push(typed_listener);
488    }
489
490    /// Publishes an event to all registered listeners with context access
491    ///
492    /// This method publishes the event to both regular listeners and context-aware listeners.
493    /// Context-aware listeners will receive a reference to the ApplicationContext.
494    ///
495    /// # Arguments
496    ///
497    /// * `event` - The event to publish
498    /// * `context` - Reference to the ApplicationContext
499    pub fn publish_with_context<T: Event + 'static>(
500        &self,
501        event: &T,
502        context: &crate::context::ApplicationContext,
503    ) {
504        let type_id = TypeId::of::<T>();
505
506        // Publish to regular listeners
507        if let Some(listeners) = self.listeners.get(&type_id) {
508            for listener in listeners.iter() {
509                listener.handle_event(event);
510            }
511        }
512
513        // Publish to context-aware listeners
514        if let Some(context_listeners) = self.context_aware_listeners.get(&type_id) {
515            for listener in context_listeners.iter() {
516                listener.handle_context_event(event, context);
517            }
518        }
519    }
520    ///
521    /// # Arguments
522    ///
523    /// * `event` - The event to publish
524    ///
525    /// # Examples
526    ///
527    /// ```rust
528    /// use verdure_context::{EventPublisher, Event, EventListener};
529    /// use std::any::Any;
530    ///
531    /// #[derive(Debug, Clone)]
532    /// struct NotificationEvent {
533    ///     message: String,
534    /// }
535    ///
536    /// impl Event for NotificationEvent {
537    ///     fn name(&self) -> &'static str { "Notification" }
538    ///     fn as_any(&self) -> &dyn Any { self }
539    ///     fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
540    /// }
541    ///
542    /// let publisher = EventPublisher::new();
543    /// let event = NotificationEvent {
544    ///     message: "System startup complete".to_string(),
545    /// };
546    ///
547    /// publisher.publish(&event);
548    /// ```
549    pub fn publish<T: Event + 'static>(&self, event: &T) {
550        let type_id = TypeId::of::<T>();
551
552        if let Some(listeners) = self.listeners.get(&type_id) {
553            for listener in listeners.iter() {
554                listener.handle_event(event);
555            }
556        }
557    }
558
559    /// Gets the number of listeners for a specific event type
560    ///
561    /// # Returns
562    ///
563    /// The number of listeners registered for the event type `T`
564    ///
565    /// # Examples
566    ///
567    /// ```rust
568    /// use verdure_context::{EventPublisher, Event, EventListener};
569    /// use std::any::Any;
570    ///
571    /// #[derive(Debug, Clone)]
572    /// struct CountEvent;
573    ///
574    /// impl Event for CountEvent {
575    ///     fn name(&self) -> &'static str { "CountEvent" }
576    ///     fn as_any(&self) -> &dyn Any { self }
577    ///     fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
578    /// }
579    ///
580    /// struct CountListener;
581    ///
582    /// impl EventListener<CountEvent> for CountListener {
583    ///     fn on_event(&self, _event: &CountEvent) {}
584    /// }
585    ///
586    /// let mut publisher = EventPublisher::new();
587    /// assert_eq!(publisher.listener_count::<CountEvent>(), 0);
588    ///
589    /// publisher.subscribe(CountListener);
590    /// assert_eq!(publisher.listener_count::<CountEvent>(), 1);
591    /// ```
592    pub fn listener_count<T: Event + 'static>(&self) -> usize {
593        let type_id = TypeId::of::<T>();
594        self.listeners
595            .get(&type_id)
596            .map(|listeners| listeners.len())
597            .unwrap_or(0)
598    }
599
600    /// Removes all listeners for all event types
601    ///
602    /// # Examples
603    ///
604    /// ```rust
605    /// use verdure_context::EventPublisher;
606    ///
607    /// let mut publisher = EventPublisher::new();
608    /// publisher.clear_all_listeners();
609    /// ```
610    pub fn clear_all_listeners(&mut self) {
611        self.listeners.clear();
612    }
613
614    /// Gets statistics about registered listeners
615    ///
616    /// # Returns
617    ///
618    /// A map of event type names to listener counts
619    ///
620    /// # Examples
621    ///
622    /// ```rust
623    /// use verdure_context::EventPublisher;
624    ///
625    /// let publisher = EventPublisher::new();
626    /// let stats = publisher.listener_statistics();
627    /// println!("Listener statistics: {:?}", stats);
628    /// ```
629    pub fn listener_statistics(&self) -> HashMap<String, usize> {
630        let mut stats = HashMap::new();
631
632        for entry in self.listeners.iter() {
633            let type_id = entry.key();
634            let listeners = entry.value();
635
636            // We can't easily get the type name from TypeId, so we'll use the TypeId debug format
637            let type_name = format!("{:?}", type_id);
638            stats.insert(type_name, listeners.len());
639        }
640
641        stats
642    }
643}
644
645impl Default for EventPublisher {
646    fn default() -> Self {
647        Self::new()
648    }
649}
650
651#[cfg(test)]
652mod tests {
653    use super::*;
654    use std::sync::atomic::{AtomicUsize, Ordering};
655
656    #[derive(Debug, Clone)]
657    struct TestEvent {
658        message: String,
659    }
660
661    impl Event for TestEvent {
662        fn name(&self) -> &'static str {
663            "TestEvent"
664        }
665
666        fn as_any(&self) -> &dyn Any {
667            self
668        }
669
670        fn into_any(self: Box<Self>) -> Box<dyn Any> {
671            self
672        }
673    }
674
675    #[derive(Debug, Clone)]
676    struct AnotherEvent {
677        value: i32,
678    }
679
680    impl Event for AnotherEvent {
681        fn name(&self) -> &'static str {
682            "AnotherEvent"
683        }
684
685        fn as_any(&self) -> &dyn Any {
686            self
687        }
688
689        fn into_any(self: Box<Self>) -> Box<dyn Any> {
690            self
691        }
692    }
693
694    static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
695
696    struct TestListener;
697
698    impl EventListener<TestEvent> for TestListener {
699        fn on_event(&self, _event: &TestEvent) {
700            TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
701        }
702    }
703
704    struct AnotherListener;
705
706    impl EventListener<AnotherEvent> for AnotherListener {
707        fn on_event(&self, _event: &AnotherEvent) {
708            TEST_COUNTER.fetch_add(10, Ordering::SeqCst);
709        }
710    }
711
712    #[test]
713    fn test_event_publisher_creation() {
714        let publisher = EventPublisher::new();
715        assert_eq!(publisher.listener_count::<TestEvent>(), 0);
716    }
717
718    #[test]
719    fn test_event_subscription() {
720        let publisher = EventPublisher::new();
721        publisher.subscribe(TestListener);
722
723        assert_eq!(publisher.listener_count::<TestEvent>(), 1);
724        assert_eq!(publisher.listener_count::<AnotherEvent>(), 0);
725    }
726
727    #[test]
728    fn test_event_publishing() {
729        TEST_COUNTER.store(0, Ordering::SeqCst);
730
731        let publisher = EventPublisher::new();
732        publisher.subscribe(TestListener);
733
734        let event = TestEvent {
735            message: "test".to_string(),
736        };
737
738        publisher.publish(&event);
739
740        assert_eq!(TEST_COUNTER.load(Ordering::SeqCst), 1);
741    }
742
743    #[test]
744    fn test_multiple_listeners_same_event() {
745        TEST_COUNTER.store(0, Ordering::SeqCst);
746
747        let publisher = EventPublisher::new();
748        publisher.subscribe(TestListener);
749        publisher.subscribe(TestListener);
750
751        let event = TestEvent {
752            message: "test".to_string(),
753        };
754
755        publisher.publish(&event);
756
757        assert_eq!(TEST_COUNTER.load(Ordering::SeqCst), 2);
758        assert_eq!(publisher.listener_count::<TestEvent>(), 2);
759    }
760
761    #[test]
762    fn test_different_event_types() {
763        TEST_COUNTER.store(0, Ordering::SeqCst);
764
765        let publisher = EventPublisher::new();
766        publisher.subscribe(TestListener);
767        publisher.subscribe(AnotherListener);
768
769        let test_event = TestEvent {
770            message: "test".to_string(),
771        };
772        let another_event = AnotherEvent { value: 42 };
773
774        publisher.publish(&test_event);
775        publisher.publish(&another_event);
776
777        // TestListener adds 1, AnotherListener adds 10
778        assert_eq!(TEST_COUNTER.load(Ordering::SeqCst), 11);
779    }
780
781    #[test]
782    fn test_event_without_listeners() {
783        let publisher = EventPublisher::new();
784        let event = TestEvent {
785            message: "no listeners".to_string(),
786        };
787
788        // Should not panic
789        publisher.publish(&event);
790    }
791
792    #[test]
793    fn test_clear_listeners() {
794        let mut publisher = EventPublisher::new();
795        publisher.subscribe(TestListener);
796        publisher.subscribe(AnotherListener);
797
798        assert_eq!(publisher.listener_count::<TestEvent>(), 1);
799        assert_eq!(publisher.listener_count::<AnotherEvent>(), 1);
800
801        publisher.clear_all_listeners();
802
803        assert_eq!(publisher.listener_count::<TestEvent>(), 0);
804        assert_eq!(publisher.listener_count::<AnotherEvent>(), 0);
805    }
806
807    #[test]
808    fn test_listener_statistics() {
809        let publisher = EventPublisher::new();
810        publisher.subscribe(TestListener);
811        publisher.subscribe(AnotherListener);
812
813        let stats = publisher.listener_statistics();
814        assert_eq!(stats.len(), 2);
815    }
816
817    #[test]
818    fn test_event_trait_methods() {
819        let event = TestEvent {
820            message: "test".to_string(),
821        };
822
823        assert_eq!(event.name(), "TestEvent");
824        assert!(event.as_any().is::<TestEvent>());
825    }
826}