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}