Skip to main content

verdure_context/
lib.rs

1//! Verdure Application Context - Context Management for the Verdure Ecosystem
2//!
3//! This crate provides application context management as a core part of the Verdure ecosystem
4//! framework. It serves as the central hub for application-wide state, configuration,
5//! and environment management that integrates with all other Verdure modules.
6//!
7//! This module provides the foundation
8//! for configuration-driven development and environment-aware component behavior
9//! across the entire Verdure ecosystem.
10//!
11//! # Core Features
12//!
13//! * **Application Context**: Centralized application state management
14//! * **Configuration Management**: Hierarchical configuration system with multiple sources
15//! * **Event Broadcasting**: Application-wide event system for decoupled communication
16//! * **IoC Integration**: Seamless integration with the Verdure IoC container
17//! * **Type-Safe Configuration**: Strongly-typed configuration value access
18//!
19//! # Quick Start
20//!
21//! ## Basic Usage
22//!
23//! ```rust
24//! use verdure_context::{ApplicationContext, ConfigSource};
25//! use std::collections::HashMap;
26//!
27//! // Create and configure application context
28//! let context = ApplicationContext::builder()
29//!     .with_property("app.name", "MyApp")
30//!     .with_property("app.port", "8080")
31//!     .build()
32//!     .unwrap();
33//!
34//! // Initialize the context
35//! context.initialize().unwrap();
36//!
37//! // Access configuration
38//! let app_name = context.get_config("app.name");
39//! let port: i64 = context.get_config_as("app.port").unwrap();
40//!
41//! println!("Starting {} on port {}", app_name, port);
42//! ```
43//!
44//! ## Configuration from Files
45//!
46//! Verdure Context supports multiple configuration file formats:
47//!
48//! ### TOML Configuration
49//!
50//! ```rust,no_run
51//! use verdure_context::ApplicationContext;
52//!
53//! let context = ApplicationContext::builder()
54//!     .with_toml_config_file("config/app.toml")
55//!     .build()
56//!     .unwrap();
57//! ```
58//!
59//! ### YAML Configuration
60//!
61//! ```rust,no_run
62//! use verdure_context::ApplicationContext;
63//!
64//! let context = ApplicationContext::builder()
65//!     .with_yaml_config_file("config/app.yaml")
66//!     .build()
67//!     .unwrap();
68//! ```
69//!
70//! ### Properties Configuration
71//!
72//! ```rust,no_run
73//! use verdure_context::ApplicationContext;
74//!
75//! let context = ApplicationContext::builder()
76//!     .with_properties_config_file("config/app.properties")
77//!     .build()
78//!     .unwrap();
79//! ```
80//!
81//! ### Auto-Detection
82//!
83//! ```rust,no_run
84//! use verdure_context::ApplicationContext;
85//!
86//! // Format is auto-detected based on file extension
87//! let context = ApplicationContext::builder()
88//!     .with_config_file("config/app.yaml")      // YAML
89//!     .with_config_file("config/db.properties") // Properties
90//!     .with_config_file("config/server.toml")   // TOML
91//!     .build()
92//!     .unwrap();
93//! ```
94//!
95//! ## Event System
96//!
97//! ```rust
98//! use verdure_context::{ApplicationContext, Event, EventListener};
99//! use std::any::Any;
100//!
101//! // Define an event
102//! #[derive(Debug, Clone)]
103//! struct UserRegisteredEvent {
104//!     pub user_id: u64,
105//!     pub email: String,
106//! }
107//!
108//! impl Event for UserRegisteredEvent {
109//!     fn name(&self) -> &'static str {
110//!         "UserRegistered"
111//!     }
112//!     
113//!     fn as_any(&self) -> &dyn Any {
114//!         self
115//!     }
116//!     
117//!     fn into_any(self: Box<Self>) -> Box<dyn Any> {
118//!         self
119//!     }
120//! }
121//!
122//! // Create event listener
123//! struct EmailNotificationListener;
124//!
125//! impl EventListener<UserRegisteredEvent> for EmailNotificationListener {
126//!     fn on_event(&self, event: &UserRegisteredEvent) {
127//!         println!("Sending welcome email to user {} ({})",
128//!                  event.user_id, event.email);
129//!     }
130//! }
131//!
132//! // Set up context with event handling
133//! let mut context = ApplicationContext::new();
134//! context.subscribe_to_events(EmailNotificationListener);
135//!
136//! // Publish events
137//! let event = UserRegisteredEvent {
138//!     user_id: 123,
139//!     email: "user@example.com".to_string(),
140//! };
141//! context.publish_event(&event);
142//! ```
143//!
144//! ## IoC Container Integration
145//!
146//! ```rust
147//! use verdure_context::ApplicationContext;
148//! use std::sync::Arc;
149//!
150//! #[derive(Debug)]
151//! struct DatabaseService {
152//!     connection_url: String,
153//! }
154//!
155//! let context = ApplicationContext::builder()
156//!     .with_property("database.url", "postgres://localhost/myapp")
157//!     .build()
158//!     .unwrap();
159//!
160//! // Register components with the container
161//! let db_service = Arc::new(DatabaseService {
162//!     connection_url: context.get_config("database.url"),
163//! });
164//! context.container().register_component(db_service);
165//!
166//! // Retrieve components
167//! let retrieved: Option<Arc<DatabaseService>> = context.get_component();
168//! assert!(retrieved.is_some());
169//! ```
170//!
171//! # Advanced Features
172//!
173//! ## Configuration Sources Priority
174//!
175//! Configuration sources are resolved in the following order (highest to lowest precedence):
176//!
177//! 1. **Runtime Properties**: Values set via `set_config()`
178//! 2. **Configuration Sources**: Sources added via `add_config_source()` (last added wins)
179//! 3. **Environment Variables**: System environment variables
180//! 4. **Configuration Files**: Files loaded via various methods (last added wins)
181//!    - TOML files (`.toml`)
182//!    - YAML files (`.yaml`, `.yml`)
183//!    - Properties files (`.properties`)
184//!
185//! ## Supported Configuration Formats
186//!
187//! ### TOML Format Example
188//!
189//! ```toml
190//! # app.toml
191//! [app]
192//! name = "MyApplication"
193//! port = 8080
194//! debug = true
195//!
196//! [database]
197//! host = "localhost"
198//! port = 5432
199//! name = "myapp"
200//! ```
201//!
202//! ### YAML Format Example
203//!
204//! ```yaml
205//! # app.yaml
206//! app:
207//!   name: MyApplication
208//!   port: 8080
209//!   debug: true
210//!   features:
211//!     - auth
212//!     - logging
213//!
214//! database:
215//!   host: localhost
216//!   port: 5432
217//!   name: myapp
218//! ```
219//!
220//! ### Properties Format Example
221//!
222//! ```properties
223//! # app.properties
224//! app.name=MyApplication
225//! app.port=8080
226//! app.debug=true
227//!
228//! database.host=localhost
229//! database.port=5432
230//! database.name=myapp
231//! ```
232//!
233//! All formats are converted to a flat key-value structure using dot notation
234//! (e.g., `app.name`, `database.host`) for consistent access patterns.
235//!
236//! # Ecosystem Context Events
237//!
238//! The context system publishes several built-in events that applications can listen to.
239//! There are two ways to listen to events:
240//!
241//! 1. **Regular Event Listeners**: Receive only the event data
242//! 2. **Context-Aware Event Listeners**: Receive both the event and ApplicationContext reference
243//!
244//! ## Built-in Lifecycle Events
245//!
246//! ### ContextInitializingEvent
247//!
248//! **When**: Fired at the very beginning of context initialization, before any actual work begins.  
249//! **Purpose**: Allows listeners to prepare for context startup or log initialization start.  
250//! **Data**: Configuration sources count, active profiles count, and timestamp.
251//!
252//! ### ContextInitializedEvent  
253//!
254//! **When**: Fired after the context is fully initialized, including all configuration sources, profiles, and IoC container.  
255//! **Purpose**: Ideal for application startup tasks that require a fully configured context.  
256//! **Data**: Final configuration sources count, active profiles count, and timestamp.
257//!
258//! ### ProfileActivatedEvent
259//!
260//! **When**: Fired whenever a profile is activated during context building.  
261//! **Purpose**: Allows listeners to react to environment changes or profile-specific setup.  
262//! **Data**: Profile name, properties count in the profile, and timestamp.
263//!
264//! ### ConfigurationChangedEvent
265//!
266//! **When**: Fired when configuration values are updated at runtime after context initialization.  
267//! **Purpose**: Enables reactive configuration updates and change tracking.  
268//! **Data**: Configuration key, old value (if any), new value, and timestamp.
269//!
270//! ## Context-Aware Event Listeners (Recommended for Lifecycle Events)
271//!
272//! Context-aware listeners can access the ApplicationContext during event handling,
273//! making them perfect for lifecycle events where you need to interact with the context:
274//!
275//! ```rust
276//! use verdure_context::{ApplicationContext, ContextInitializedEvent, ContextAwareEventListener};
277//!
278//! struct StartupTasks;
279//!
280//! impl ContextAwareEventListener<ContextInitializedEvent> for StartupTasks {
281//!     fn on_context_event(&self, event: &ContextInitializedEvent, context: &ApplicationContext) {
282//!         println!("🚀 Context initialized with {} sources!", event.config_sources_count);
283//!         
284//!         // Access configuration
285//!         let app_name = context.get_config("app.name");
286//!         println!("📱 Starting application: {}", app_name);
287//!         
288//!         // Access IoC container for dependency injection setup
289//!         let container = context.container();
290//!         // Setup your components...
291//!         
292//!         // Check environment
293//!         let env = context.environment();
294//!         println!("🌍 Running in {} environment", env);
295//!     }
296//! }
297//!
298//! let mut context = ApplicationContext::builder()
299//!     .with_property("app.name", "MyApp")
300//!     .build()
301//!     .unwrap();
302//!
303//! context.subscribe_to_context_events(StartupTasks);
304//! context.initialize().unwrap(); // Triggers the context-aware listener
305//! ```
306//!
307//! ## ContextInitializingEvent Usage
308//!
309//! Listen to preparation phases before initialization:
310//!
311//! ```rust
312//! use verdure_context::{ApplicationContext, ContextInitializingEvent, ContextAwareEventListener};
313//!
314//! struct PreStartupListener;
315//!
316//! impl ContextAwareEventListener<ContextInitializingEvent> for PreStartupListener {
317//!     fn on_context_event(&self, event: &ContextInitializingEvent, context: &ApplicationContext) {
318//!         println!("🔧 Context initializing with {} sources...",
319//!                  event.config_sources_count);
320//!         
321//!         // Pre-initialization tasks
322//!         let startup_time = event.timestamp;
323//!         println!("⏰ Startup began at: {:?}", startup_time);
324//!     }
325//! }
326//! ```
327//!
328//!
329//! ## ConfigurationChangedEvent Usage
330//!
331//! Track runtime configuration changes:
332//!
333//! ```rust
334//! use verdure_context::{ApplicationContext, ConfigurationChangedEvent, EventListener};
335//!
336//! struct ConfigListener;
337//!
338//! impl EventListener<ConfigurationChangedEvent> for ConfigListener {
339//!     fn on_event(&self, event: &ConfigurationChangedEvent) {
340//!         match &event.old_value {
341//!             Some(old) => println!("⚙️ Configuration '{}' changed from '{}' to '{}'",
342//!                                   event.key, old, event.new_value),
343//!             None => println!("➕ Configuration '{}' set to '{}'",
344//!                              event.key, event.new_value),
345//!         }
346//!     }
347//! }
348//!
349//! let mut context = ApplicationContext::new();
350//! context.subscribe_to_events(ConfigListener);
351//! context.set_config("app.mode", "production");
352//! ```
353//!
354//! ## Event System Architecture
355//!
356//! The event system supports both regular and context-aware listeners simultaneously:
357//!
358//! - **Regular listeners** (`EventListener<T>`) receive only the event data
359//! - **Context-aware listeners** (`ContextAwareEventListener<T>`) receive both event data and ApplicationContext reference
360//! - Both types can be registered for the same event type
361//! - Events are published to all registered listeners of the appropriate type
362//! - Lifecycle events automatically provide context access for enhanced integration capabilities
363
364pub mod config;
365pub mod context;
366pub mod error;
367pub mod event;
368
369// Re-export main types for convenience
370pub use config::{ConfigManager, ConfigSource, ConfigValue};
371pub use context::{ApplicationContext, ApplicationContextBuilder};
372pub use error::{ContextError, ContextResult};
373pub use event::{
374    AnyContextAwareEventListener, AnyEventListener, ConfigurationChangedEvent,
375    ContextAwareEventListener, ContextInitializedEvent, ContextInitializingEvent, Event,
376    EventListener, EventPublisher,
377};