verdure_context/context.rs
1//! Application context implementation
2//!
3//! This module provides the main `ApplicationContext` implementation that serves as the
4//! central hub for application-wide state, configuration, environment management, and
5//! integration with the IoC container.
6
7use crate::config::{ConfigFactory, ConfigManager, ConfigSource, ConfigValue};
8use crate::error::{ContextError, ContextResult};
9use crate::event::{
10 ConfigurationChangedEvent, ContextAwareEventListener, ContextInitializedEvent,
11 ContextInitializingEvent, Event, EventListener, EventPublisher,
12};
13use dashmap::DashMap;
14use std::path::Path;
15use std::sync::Arc;
16use verdure_ioc::{ComponentContainer, ComponentFactory, ComponentInstance};
17
18/// Application context builder
19///
20/// `ApplicationContextBuilder` provides a fluent API for constructing
21/// an `ApplicationContext` with various configuration sources, profiles,
22/// and settings.
23///
24/// # Examples
25///
26/// ```rust
27/// use verdure_context::ApplicationContextBuilder;
28///
29/// let context = ApplicationContextBuilder::new()
30/// .with_property("app.name", "MyApp")
31/// .build()
32/// .unwrap();
33/// ```
34#[derive(Debug)]
35pub struct ApplicationContextBuilder {
36 config_sources: Vec<ConfigSource>,
37 properties: std::collections::HashMap<String, String>,
38}
39
40impl ApplicationContextBuilder {
41 /// Creates a new application context builder
42 ///
43 /// # Examples
44 ///
45 /// ```rust
46 /// use verdure_context::ApplicationContextBuilder;
47 ///
48 /// let builder = ApplicationContextBuilder::new();
49 /// ```
50 pub fn new() -> Self {
51 Self {
52 config_sources: Vec::new(),
53 properties: std::collections::HashMap::new(),
54 }
55 }
56
57 /// Adds a configuration source
58 ///
59 /// # Arguments
60 ///
61 /// * `source` - The configuration source to add
62 ///
63 /// # Examples
64 ///
65 /// ```rust
66 /// use verdure_context::{ApplicationContextBuilder, ConfigSource};
67 /// use std::collections::HashMap;
68 ///
69 /// let builder = ApplicationContextBuilder::new()
70 /// .with_config_source(ConfigSource::Environment);
71 /// ```
72 pub fn with_config_source(mut self, source: ConfigSource) -> Self {
73 self.config_sources.push(source);
74 self
75 }
76
77 /// Loads configuration from a TOML file
78 ///
79 /// # Arguments
80 ///
81 /// * `path` - Path to the TOML configuration file
82 ///
83 /// # Examples
84 ///
85 /// ```rust
86 /// use verdure_context::ApplicationContextBuilder;
87 ///
88 /// let builder = ApplicationContextBuilder::new()
89 /// .with_toml_config_file("config/app.toml");
90 /// ```
91 pub fn with_toml_config_file<P: AsRef<Path>>(mut self, path: P) -> Self {
92 let path_str = path.as_ref().to_string_lossy().to_string();
93 self.config_sources.push(ConfigSource::TomlFile(path_str));
94 self
95 }
96
97 /// Loads configuration from a YAML file
98 ///
99 /// # Arguments
100 ///
101 /// * `path` - Path to the YAML configuration file
102 ///
103 /// # Examples
104 ///
105 /// ```rust
106 /// use verdure_context::ApplicationContextBuilder;
107 ///
108 /// let builder = ApplicationContextBuilder::new()
109 /// .with_yaml_config_file("config/app.yaml");
110 /// ```
111 pub fn with_yaml_config_file<P: AsRef<Path>>(mut self, path: P) -> Self {
112 let path_str = path.as_ref().to_string_lossy().to_string();
113 self.config_sources.push(ConfigSource::YamlFile(path_str));
114 self
115 }
116
117 /// Loads configuration from a Properties file
118 ///
119 /// # Arguments
120 ///
121 /// * `path` - Path to the Properties configuration file
122 ///
123 /// # Examples
124 ///
125 /// ```rust
126 /// use verdure_context::ApplicationContextBuilder;
127 ///
128 /// let builder = ApplicationContextBuilder::new()
129 /// .with_properties_config_file("config/app.properties");
130 /// ```
131 pub fn with_properties_config_file<P: AsRef<Path>>(mut self, path: P) -> Self {
132 let path_str = path.as_ref().to_string_lossy().to_string();
133 self.config_sources
134 .push(ConfigSource::PropertiesFile(path_str));
135 self
136 }
137
138 /// Loads configuration from a file with automatic format detection
139 ///
140 /// The format is detected based on the file extension:
141 /// - `.toml` -> TOML format
142 /// - `.yaml` or `.yml` -> YAML format
143 /// - `.properties` -> Properties format
144 /// - Others -> Attempts to parse as TOML first, then YAML, then Properties
145 ///
146 /// # Arguments
147 ///
148 /// * `path` - Path to the configuration file
149 ///
150 /// # Examples
151 ///
152 /// ```rust
153 /// use verdure_context::ApplicationContextBuilder;
154 ///
155 /// let builder = ApplicationContextBuilder::new()
156 /// .with_config_file("config/app.yaml")
157 /// .with_config_file("config/database.properties")
158 /// .with_config_file("config/server.toml");
159 /// ```
160 pub fn with_config_file<P: AsRef<Path>>(mut self, path: P) -> Self {
161 let path_str = path.as_ref().to_string_lossy().to_string();
162 self.config_sources.push(ConfigSource::ConfigFile(path_str));
163 self
164 }
165
166 /// Sets a property value
167 ///
168 /// # Arguments
169 ///
170 /// * `key` - The property key
171 /// * `value` - The property value
172 ///
173 /// # Examples
174 ///
175 /// ```rust
176 /// use verdure_context::ApplicationContextBuilder;
177 ///
178 /// let builder = ApplicationContextBuilder::new()
179 /// .with_property("app.name", "MyApplication");
180 /// ```
181 pub fn with_property(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
182 self.properties.insert(key.into(), value.into());
183 self
184 }
185
186 /// Builds the application context
187 ///
188 /// # Returns
189 ///
190 /// A new `ApplicationContext` instance configured with the specified settings
191 ///
192 /// # Errors
193 ///
194 /// Returns an error if context initialization fails
195 ///
196 /// # Examples
197 ///
198 /// ```rust
199 /// use verdure_context::ApplicationContextBuilder;
200 ///
201 /// let context = ApplicationContextBuilder::new()
202 /// .with_property("app.name", "MyApp")
203 /// .build()
204 /// .unwrap();
205 /// ```
206 pub fn build(self) -> ContextResult<ApplicationContext> {
207 let context = ApplicationContext::new();
208
209 // Add configuration sources
210 for source in self.config_sources {
211 context.config_manager.add_source(source)?;
212 }
213
214 // Add properties as a configuration source
215 if !self.properties.is_empty() {
216 context
217 .config_manager
218 .add_source(ConfigSource::Properties(self.properties))?;
219 }
220
221 Ok(context)
222 }
223}
224
225impl Default for ApplicationContextBuilder {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231/// Main application context
232///
233/// `ApplicationContext` serves as the central hub for application-wide services,
234/// configuration, environment management, and integration with the IoC container.
235/// It provides a unified interface for accessing all framework services.
236///
237/// # Examples
238///
239/// ```rust
240/// use verdure_context::ApplicationContext;
241///
242/// let context = ApplicationContext::builder()
243/// .with_property("app.name", "MyApp")
244/// .build()
245/// .unwrap();
246///
247/// // Access configuration
248/// let app_name = context.get_config("app.name");
249///
250/// // Access IoC container
251/// let container = context.container();
252/// ```
253pub struct ApplicationContext {
254 /// Configuration manager
255 config_manager: Arc<ConfigManager>,
256 /// Event publisher for application-wide events
257 event_publisher: EventPublisher,
258 /// IoC container integration
259 container: Arc<ComponentContainer>,
260 /// Application properties cache
261 properties_cache: DashMap<String, ConfigValue>,
262}
263
264impl ApplicationContext {
265 /// Creates a new application context
266 ///
267 /// # Examples
268 ///
269 /// ```rust
270 /// use verdure_context::ApplicationContext;
271 ///
272 /// let context = ApplicationContext::new();
273 /// ```
274 pub fn new() -> Self {
275 Self {
276 config_manager: Arc::new(ConfigManager::new()),
277 event_publisher: EventPublisher::new(),
278 container: Arc::new(ComponentContainer::new()),
279 properties_cache: DashMap::new(),
280 }
281 }
282
283 /// Creates a builder for constructing an application context
284 ///
285 /// # Examples
286 ///
287 /// ```rust
288 /// use verdure_context::ApplicationContext;
289 ///
290 /// let context = ApplicationContext::builder()
291 /// .with_property("app.version", "1.0.0")
292 /// .build()
293 /// .unwrap();
294 /// ```
295 pub fn builder() -> ApplicationContextBuilder {
296 ApplicationContextBuilder::new()
297 }
298 fn initialize_early(&self) -> ContextResult<()> {
299 self.container.register_component(self.config_manager.clone());
300
301 for factory in inventory::iter::<ConfigFactory> {
302 let config_component = (factory.create_fn)(self.config_manager.clone())?;
303 self.container.register_component(config_component);
304 }
305 Ok(())
306 }
307 /// Initializes the application context
308 ///
309 /// This method initializes the IoC container and performs any other
310 /// necessary initialization steps.
311 ///
312 /// # Returns
313 ///
314 /// `Ok(())` if initialization succeeds, an error otherwise
315 ///
316 /// # Examples
317 ///
318 /// ```rust
319 /// use verdure_context::ApplicationContext;
320 ///
321 /// let context = ApplicationContext::new();
322 /// context.initialize().unwrap();
323 /// ```
324 pub fn initialize(&self) -> ContextResult<()> {
325 self.initialize_early()?;
326 // Publish context-initializing event at the start
327 let initializing_event = ContextInitializingEvent {
328 config_sources_count: self.config_manager.sources_count(),
329 timestamp: std::time::SystemTime::now(),
330 };
331 self.event_publisher
332 .publish_with_context(&initializing_event, self);
333
334 // Initialize the IoC container
335 self.container.initialize().map_err(|e| {
336 ContextError::initialization_failed(format!(
337 "IoC container initialization failed: {}",
338 e
339 ))
340 })?;
341
342 // Publish context initialized event at the end
343 let initialized_event = ContextInitializedEvent {
344 config_sources_count: self.config_manager.sources_count(),
345 timestamp: std::time::SystemTime::now(),
346 };
347 self.event_publisher
348 .publish_with_context(&initialized_event, self);
349
350 Ok(())
351 }
352
353 /// Gets a configuration value by key
354 ///
355 /// # Arguments
356 ///
357 /// * `key` - The configuration key
358 ///
359 /// # Returns
360 ///
361 /// The configuration value as a string, or an empty string if not found
362 ///
363 /// # Examples
364 ///
365 /// ```rust
366 /// use verdure_context::{ApplicationContext, ConfigSource};
367 /// use std::collections::HashMap;
368 ///
369 /// let mut context = ApplicationContext::new();
370 ///
371 /// let mut props = HashMap::new();
372 /// props.insert("app.name".to_string(), "MyApp".to_string());
373 /// context.add_config_source(ConfigSource::Properties(props)).unwrap();
374 ///
375 /// assert_eq!(context.get_config("app.name"), "MyApp");
376 /// ```
377 pub fn get_config(&self, key: &str) -> String {
378 self.config_manager.get_string_or_default(key, "")
379 }
380
381 /// Gets a configuration value as a specific type
382 ///
383 /// # Arguments
384 ///
385 /// * `key` - The configuration key
386 ///
387 /// # Returns
388 ///
389 /// The configuration value parsed as the requested type
390 ///
391 /// # Errors
392 ///
393 /// Returns an error if the key is not found or cannot be parsed as the requested type
394 ///
395 /// # Examples
396 ///
397 /// ```rust
398 /// use verdure_context::{ApplicationContext, ConfigSource};
399 /// use std::collections::HashMap;
400 ///
401 /// let mut context = ApplicationContext::new();
402 ///
403 /// let mut props = HashMap::new();
404 /// props.insert("app.port".to_string(), "8080".to_string());
405 /// context.add_config_source(ConfigSource::Properties(props)).unwrap();
406 ///
407 /// let port: i64 = context.get_config_as("app.port").unwrap();
408 /// assert_eq!(port, 8080);
409 /// ```
410 pub fn get_config_as<T>(&self, key: &str) -> ContextResult<T>
411 where
412 T: std::str::FromStr,
413 T::Err: std::fmt::Display,
414 {
415 let value = self.config_manager.get_string(key)?;
416 value
417 .parse::<T>()
418 .map_err(|e| ContextError::invalid_configuration(key, e.to_string()))
419 }
420
421 /// Gets a configuration value with a default
422 ///
423 /// # Arguments
424 ///
425 /// * `key` - The configuration key
426 /// * `default` - The default value to return if key is not found
427 ///
428 /// # Examples
429 ///
430 /// ```rust
431 /// use verdure_context::ApplicationContext;
432 ///
433 /// let context = ApplicationContext::new();
434 /// let port = context.get_config_or_default("app.port", "8080");
435 /// assert_eq!(port, "8080");
436 /// ```
437 pub fn get_config_or_default(&self, key: &str, default: &str) -> String {
438 self.config_manager.get_string_or_default(key, default)
439 }
440
441 /// Sets a configuration property
442 ///
443 /// # Arguments
444 ///
445 /// * `key` - The configuration key
446 /// * `value` - The configuration value
447 ///
448 /// # Examples
449 ///
450 /// ```rust
451 /// use verdure_context::ApplicationContext;
452 ///
453 /// let mut context = ApplicationContext::new();
454 /// context.set_config("runtime.property", "runtime.value");
455 ///
456 /// assert_eq!(context.get_config("runtime.property"), "runtime.value");
457 /// ```
458 /// Sets a configuration property
459 pub fn set_config(&self, key: &str, value: &str) {
460 let old_value = self.get_config(key);
461 let old_value_opt = if old_value.is_empty() {
462 None
463 } else {
464 Some(old_value)
465 };
466
467 self.config_manager
468 .set(key, ConfigValue::String(value.to_string()));
469
470 let event = ConfigurationChangedEvent {
471 key: key.to_string(),
472 old_value: old_value_opt,
473 new_value: value.to_string(),
474 timestamp: std::time::SystemTime::now(),
475 };
476 self.event_publisher.publish(&event);
477 }
478
479 /// Adds a configuration source
480 pub fn add_config_source(&self, source: ConfigSource) -> ContextResult<()> {
481 self.config_manager.add_source(source)
482 }
483
484 /// Gets the IoC container
485 ///
486 /// # Returns
487 ///
488 /// A reference to the IoC container
489 ///
490 /// # Examples
491 ///
492 /// ```rust
493 /// use verdure_context::ApplicationContext;
494 ///
495 /// let context = ApplicationContext::new();
496 /// let container = context.container();
497 /// ```
498 pub fn container(&self) -> Arc<ComponentContainer> {
499 self.container.clone()
500 }
501
502 /// Gets a shared reference to the ConfigManager for IoC registration
503 pub fn config_manager(&self) -> Arc<ConfigManager> {
504 self.config_manager.clone()
505 }
506
507 /// Gets a component from the IoC container
508 ///
509 /// # Returns
510 ///
511 /// The requested component if found
512 ///
513 /// # Examples
514 ///
515 /// ```rust
516 /// use verdure_context::ApplicationContext;
517 /// use std::sync::Arc;
518 ///
519 /// #[derive(Debug)]
520 /// struct MyService {
521 /// name: String,
522 /// }
523 ///
524 /// let context = ApplicationContext::new();
525 ///
526 /// // First register the component
527 /// let service = Arc::new(MyService {
528 /// name: "TestService".to_string(),
529 /// });
530 /// context.container().register_component(service);
531 ///
532 /// // Then retrieve it
533 /// let retrieved: Option<Arc<MyService>> = context.get_component();
534 /// assert!(retrieved.is_some());
535 /// ```
536 pub fn get_component<T: 'static + Send + Sync>(&self) -> Option<Arc<T>> {
537 self.container.get_component()
538 }
539 /// Registers a pre-created component instance with the context container
540 pub fn register_component(&self, instance: ComponentInstance) {
541 self.container.register_component(instance)
542 }
543
544 /// Publishes an event
545 ///
546 /// # Arguments
547 ///
548 /// * `event` - The event to publish
549 ///
550 /// # Examples
551 ///
552 /// ```rust
553 /// use verdure_context::{ApplicationContext, Event};
554 /// use std::any::Any;
555 ///
556 /// #[derive(Debug, Clone)]
557 /// struct MyEvent {
558 /// message: String,
559 /// }
560 ///
561 /// impl Event for MyEvent {
562 /// fn name(&self) -> &'static str { "MyEvent" }
563 /// fn as_any(&self) -> &dyn Any { self }
564 /// fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
565 /// }
566 ///
567 /// let context = ApplicationContext::new();
568 /// let event = MyEvent {
569 /// message: "Hello, World!".to_string(),
570 /// };
571 ///
572 /// context.publish_event(&event);
573 /// ```
574 pub fn publish_event<T: Event + 'static>(&self, event: &T) {
575 self.event_publisher.publish(event);
576 }
577
578 /// Subscribes to events with context access
579 ///
580 /// Context-aware listeners receive both the event and a reference to the ApplicationContext,
581 /// allowing them to interact with the context during event handling.
582 ///
583 /// # Arguments
584 ///
585 /// * `listener` - The context-aware event listener to register
586 ///
587 /// # Examples
588 ///
589 /// ```rust
590 /// use verdure_context::{ApplicationContext, ContextInitializedEvent, ContextAwareEventListener};
591 ///
592 /// struct StartupListener;
593 ///
594 /// impl ContextAwareEventListener<ContextInitializedEvent> for StartupListener {
595 /// fn on_context_event(&self, event: &ContextInitializedEvent, context: &ApplicationContext) {
596 /// println!("Context initialized! App name: {}", context.get_config("app.name"));
597 /// println!("Environment: {}", context.environment());
598 /// }
599 /// }
600 ///
601 /// let mut context = ApplicationContext::new();
602 /// context.subscribe_to_context_events(StartupListener);
603 /// ```
604 /// Subscribes to events with context access
605 pub fn subscribe_to_context_events<
606 T: Event + 'static,
607 L: ContextAwareEventListener<T> + 'static,
608 >(
609 &self,
610 listener: L,
611 ) {
612 self.event_publisher.subscribe_context_aware(listener);
613 }
614 ///
615 /// # Arguments
616 ///
617 /// * `listener` - The event listener to register
618 ///
619 /// # Examples
620 ///
621 /// ```rust
622 /// use verdure_context::{ApplicationContext, Event, EventListener};
623 /// use std::any::Any;
624 ///
625 /// #[derive(Debug, Clone)]
626 /// struct TestEvent;
627 ///
628 /// impl Event for TestEvent {
629 /// fn name(&self) -> &'static str { "TestEvent" }
630 /// fn as_any(&self) -> &dyn Any { self }
631 /// fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
632 /// }
633 ///
634 /// struct TestListener;
635 ///
636 /// impl EventListener<TestEvent> for TestListener {
637 /// fn on_event(&self, _event: &TestEvent) {
638 /// println!("Event received!");
639 /// }
640 /// }
641 ///
642 /// let mut context = ApplicationContext::new();
643 /// context.subscribe_to_events(TestListener);
644 /// ```
645 /// Subscribes to events
646 pub fn subscribe_to_events<T: Event + 'static, L: EventListener<T> + 'static>(
647 &self,
648 listener: L,
649 ) {
650 self.event_publisher.subscribe(listener);
651 }
652
653 /// Gets environment information
654 ///
655 /// # Returns
656 ///
657 /// A string representing the current environment
658 ///
659 /// # Examples
660 ///
661 /// ```rust
662 /// use verdure_context::ApplicationContext;
663 ///
664 /// let context = ApplicationContext::new();
665 /// println!("Environment: {}", context.environment());
666 /// ```
667 pub fn environment(&self) -> String {
668 "default".to_string()
669 }
670}
671
672impl Default for ApplicationContext {
673 fn default() -> Self {
674 Self::new()
675 }
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681 use std::collections::HashMap;
682
683 #[test]
684 fn test_application_context_creation() {
685 let context = ApplicationContext::new();
686 // Context created successfully
687 assert_eq!(context.environment(), "default");
688 }
689
690 #[test]
691 fn test_application_context_builder() {
692 let context = ApplicationContext::builder()
693 .with_property("app.name", "TestApp")
694 .build()
695 .unwrap();
696
697 assert_eq!(context.get_config("app.name"), "TestApp");
698 }
699
700 #[test]
701 fn test_configuration_management() {
702 let context = ApplicationContext::new();
703
704 let mut props = HashMap::new();
705 props.insert(
706 "database.url".to_string(),
707 "postgres://localhost/test".to_string(),
708 );
709 props.insert("server.port".to_string(), "3000".to_string());
710 props.insert("debug.enabled".to_string(), "true".to_string());
711
712 context
713 .add_config_source(ConfigSource::Properties(props))
714 .unwrap();
715
716 assert_eq!(
717 context.get_config("database.url"),
718 "postgres://localhost/test"
719 );
720
721 let port: i64 = context.get_config_as("server.port").unwrap();
722 assert_eq!(port, 3000);
723
724 let debug: bool = context.get_config_as("debug.enabled").unwrap();
725 assert_eq!(debug, true);
726 }
727
728 #[test]
729 fn test_configuration_with_defaults() {
730 let context = ApplicationContext::new();
731
732 assert_eq!(context.get_config("missing.key"), "");
733 assert_eq!(
734 context.get_config_or_default("missing.key", "default"),
735 "default"
736 );
737 }
738
739 #[test]
740 fn test_runtime_configuration() {
741 let context = ApplicationContext::new();
742
743 context.set_config("runtime.property", "runtime.value");
744 assert_eq!(context.get_config("runtime.property"), "runtime.value");
745 }
746
747
748 #[test]
749 fn test_environment_default() {
750 let context = ApplicationContext::new();
751 assert_eq!(context.environment(), "default");
752 }
753
754 #[test]
755 fn test_container_integration() {
756 let context = ApplicationContext::new();
757 let container = context.container();
758
759 // Test that we can access the container
760 assert!(container.get_component::<String>().is_none());
761 }
762
763 // Test event system integration
764 use std::any::Any;
765
766 #[derive(Debug, Clone)]
767 struct TestContextEvent {
768 message: String,
769 }
770
771 impl Event for TestContextEvent {
772 fn name(&self) -> &'static str {
773 "TestContextEvent"
774 }
775
776 fn as_any(&self) -> &dyn Any {
777 self
778 }
779
780 fn into_any(self: Box<Self>) -> Box<dyn Any> {
781 self
782 }
783 }
784
785 struct TestContextListener {
786 received: Arc<std::sync::Mutex<Vec<String>>>,
787 }
788
789 impl EventListener<TestContextEvent> for TestContextListener {
790 fn on_event(&self, event: &TestContextEvent) {
791 let mut received = self.received.lock().unwrap();
792 received.push(event.message.clone());
793 }
794 }
795
796 #[test]
797 fn test_event_system_integration() {
798 let received = Arc::new(std::sync::Mutex::new(Vec::new()));
799 let listener = TestContextListener {
800 received: received.clone(),
801 };
802
803 let context = ApplicationContext::new();
804 context.subscribe_to_events(listener);
805
806 let event = TestContextEvent {
807 message: "test message".to_string(),
808 };
809
810 context.publish_event(&event);
811
812 let received_messages = received.lock().unwrap();
813 assert_eq!(received_messages.len(), 1);
814 assert_eq!(received_messages[0], "test message");
815 }
816
817 #[test]
818 fn test_built_in_context_events() {
819 use crate::event::{
820 ConfigurationChangedEvent, ContextInitializedEvent, ContextInitializingEvent,
821 };
822 use std::sync::{Arc, Mutex};
823
824 // Event collectors
825 let initializing_events = Arc::new(Mutex::new(Vec::new()));
826 let initialized_events = Arc::new(Mutex::new(Vec::new()));
827 let config_events = Arc::new(Mutex::new(Vec::new()));
828
829 // Event listeners
830 struct InitializingListener(Arc<Mutex<Vec<ContextInitializingEvent>>>);
831 impl EventListener<ContextInitializingEvent> for InitializingListener {
832 fn on_event(&self, event: &ContextInitializingEvent) {
833 let mut events = self.0.lock().unwrap();
834 events.push(event.clone());
835 }
836 }
837
838 struct InitializedListener(Arc<Mutex<Vec<ContextInitializedEvent>>>);
839 impl EventListener<ContextInitializedEvent> for InitializedListener {
840 fn on_event(&self, event: &ContextInitializedEvent) {
841 let mut events = self.0.lock().unwrap();
842 events.push(event.clone());
843 }
844 }
845
846 struct ConfigListener(Arc<Mutex<Vec<ConfigurationChangedEvent>>>);
847 impl EventListener<ConfigurationChangedEvent> for ConfigListener {
848 fn on_event(&self, event: &ConfigurationChangedEvent) {
849 let mut events = self.0.lock().unwrap();
850 events.push(event.clone());
851 }
852 }
853
854 // Create context with configuration
855 let context = ApplicationContext::builder()
856 .with_property("initial.key", "initial.value")
857 .build()
858 .unwrap();
859
860 // Subscribe to events BEFORE initialization
861 context.subscribe_to_events(InitializingListener(initializing_events.clone()));
862 context.subscribe_to_events(InitializedListener(initialized_events.clone()));
863 context.subscribe_to_events(ConfigListener(config_events.clone()));
864
865 // Initialize context (should fire both ContextInitializingEvent and ContextInitializedEvent)
866 context.initialize().unwrap();
867
868 // Change configuration (should fire ConfigurationChangedEvent)
869 context.set_config("runtime.key", "runtime.value");
870 context.set_config("initial.key", "updated.value");
871
872 // Verify initializing events were fired
873 let initializing_events = initializing_events.lock().unwrap();
874 assert_eq!(initializing_events.len(), 1);
875 assert!(initializing_events[0].config_sources_count > 0);
876
877 // Verify initialized events were fired
878 let initialized_events = initialized_events.lock().unwrap();
879 assert_eq!(initialized_events.len(), 1);
880 assert!(initialized_events[0].config_sources_count > 0);
881
882 // Verify the initializing event was fired before the initialized event
883 assert!(initializing_events[0].timestamp <= initialized_events[0].timestamp);
884
885 let config_events = config_events.lock().unwrap();
886 assert_eq!(config_events.len(), 2);
887
888 // First config change (new key)
889 assert_eq!(config_events[0].key, "runtime.key");
890 assert_eq!(config_events[0].old_value, None);
891 assert_eq!(config_events[0].new_value, "runtime.value");
892
893 // Second config change (update existing key)
894 assert_eq!(config_events[1].key, "initial.key");
895 assert_eq!(
896 config_events[1].old_value,
897 Some("initial.value".to_string())
898 );
899 assert_eq!(config_events[1].new_value, "updated.value");
900 }
901
902 #[test]
903 fn test_context_aware_event_listeners() {
904 use crate::event::{ContextAwareEventListener, ContextInitializedEvent};
905 use std::sync::{Arc, Mutex};
906
907 // Track events and context access
908 let events_received = Arc::new(Mutex::new(Vec::new()));
909 let context_data_accessed = Arc::new(Mutex::new(Vec::new()));
910
911 // Context-aware event listener
912 struct ContextAwareListener {
913 events: Arc<Mutex<Vec<String>>>,
914 context_data: Arc<Mutex<Vec<String>>>,
915 }
916
917 impl ContextAwareEventListener<ContextInitializedEvent> for ContextAwareListener {
918 fn on_context_event(
919 &self,
920 event: &ContextInitializedEvent,
921 context: &crate::context::ApplicationContext,
922 ) {
923 // Record the event
924 let mut events = self.events.lock().unwrap();
925 events.push(format!(
926 "Initialized with {} sources",
927 event.config_sources_count
928 ));
929
930 // Access context data
931 let mut context_data = self.context_data.lock().unwrap();
932 context_data.push(context.get_config("test.key"));
933 context_data.push(context.environment());
934 }
935 }
936
937 // Create context with some configuration
938 let context = ApplicationContext::builder()
939 .with_property("test.key", "test.value")
940 .with_property("app.name", "TestApp")
941 .build()
942 .unwrap();
943
944 // Register context-aware listener
945 let listener = ContextAwareListener {
946 events: events_received.clone(),
947 context_data: context_data_accessed.clone(),
948 };
949 context.subscribe_to_context_events(listener);
950
951 // Initialize context - this should trigger the context-aware event
952 context.initialize().unwrap();
953
954 // Verify the context-aware listener received the event and could access context
955 let events = events_received.lock().unwrap();
956 assert_eq!(events.len(), 1);
957 assert!(events[0].contains("Initialized with"));
958
959 let context_data = context_data_accessed.lock().unwrap();
960 assert_eq!(context_data.len(), 2);
961 assert_eq!(context_data[0], "test.value"); // Successfully accessed config
962 assert_eq!(context_data[1], "default"); // Successfully accessed environment
963 }
964}