elif_core/container/
advanced_binding_examples.rs

1//! Advanced Binding Examples for IOC Phase 4
2//!
3//! This module demonstrates the advanced binding features including:
4//! - Interface-to-implementation mapping
5//! - Named services and tagging
6//! - Conditional binding (environment, features, profiles)
7//! - Factory patterns and lazy initialization
8//! - Generic type support
9//! - Collection resolution
10
11use crate::container::{AdvancedBindingBuilder, IocContainer, ServiceBinder, ServiceScope};
12use crate::errors::CoreError;
13use std::collections::HashMap;
14
15// Example interfaces and implementations
16
17/// Cache interface for different storage backends
18pub trait Cache: Send + Sync {
19    fn get(&self, key: &str) -> Option<String>;
20    fn set(&self, key: &str, value: String) -> Result<(), String>;
21    fn delete(&self, key: &str) -> Result<(), String>;
22}
23
24/// Redis cache implementation
25#[derive(Default)]
26pub struct RedisCache {
27    _config: String,
28}
29
30impl Cache for RedisCache {
31    fn get(&self, key: &str) -> Option<String> {
32        println!("RedisCache: Getting key '{}'", key);
33        Some(format!("redis_value_{}", key))
34    }
35
36    fn set(&self, key: &str, value: String) -> Result<(), String> {
37        println!("RedisCache: Setting '{}' = '{}'", key, value);
38        Ok(())
39    }
40
41    fn delete(&self, key: &str) -> Result<(), String> {
42        println!("RedisCache: Deleting key '{}'", key);
43        Ok(())
44    }
45}
46
47/// In-memory cache implementation
48#[derive(Default)]
49pub struct MemoryCache {
50    storage: HashMap<String, String>,
51}
52
53impl Cache for MemoryCache {
54    fn get(&self, key: &str) -> Option<String> {
55        println!("MemoryCache: Getting key '{}'", key);
56        self.storage.get(key).cloned()
57    }
58
59    fn set(&self, key: &str, value: String) -> Result<(), String> {
60        println!("MemoryCache: Setting '{}' = '{}'", key, value);
61        // Note: In real implementation, we'd need interior mutability
62        Ok(())
63    }
64
65    fn delete(&self, key: &str) -> Result<(), String> {
66        println!("MemoryCache: Deleting key '{}'", key);
67        Ok(())
68    }
69}
70
71/// Hybrid cache that uses both Redis and Memory
72#[derive(Default)]
73pub struct HybridCache;
74
75impl Cache for HybridCache {
76    fn get(&self, key: &str) -> Option<String> {
77        println!(
78            "HybridCache: Getting key '{}' (checking memory first, then Redis)",
79            key
80        );
81        Some(format!("hybrid_value_{}", key))
82    }
83
84    fn set(&self, key: &str, value: String) -> Result<(), String> {
85        println!(
86            "HybridCache: Setting '{}' = '{}' (both memory and Redis)",
87            key, value
88        );
89        Ok(())
90    }
91
92    fn delete(&self, key: &str) -> Result<(), String> {
93        println!(
94            "HybridCache: Deleting key '{}' (from both memory and Redis)",
95            key
96        );
97        Ok(())
98    }
99}
100
101/// Email service interface
102pub trait EmailService: Send + Sync {
103    fn send_email(&self, to: &str, subject: &str, body: &str) -> Result<(), String>;
104}
105
106/// SMTP email service
107#[derive(Default)]
108pub struct SmtpEmailService;
109
110impl EmailService for SmtpEmailService {
111    fn send_email(&self, to: &str, subject: &str, body: &str) -> Result<(), String> {
112        println!("SMTP: Sending email to '{}' with subject '{}'", to, subject);
113        println!("Body: {}", body);
114        Ok(())
115    }
116}
117
118/// SendGrid email service
119#[derive(Default)]
120pub struct SendGridEmailService;
121
122impl EmailService for SendGridEmailService {
123    fn send_email(&self, to: &str, subject: &str, body: &str) -> Result<(), String> {
124        println!(
125            "SendGrid API: Sending email to '{}' with subject '{}'",
126            to, subject
127        );
128        println!("Body: {}", body);
129        Ok(())
130    }
131}
132
133/// Storage interface
134pub trait Storage: Send + Sync {
135    fn store(&self, path: &str, data: &[u8]) -> Result<String, String>;
136    fn retrieve(&self, path: &str) -> Result<Vec<u8>, String>;
137}
138
139/// Local file storage
140#[derive(Default)]
141pub struct LocalStorage;
142
143impl Storage for LocalStorage {
144    fn store(&self, path: &str, data: &[u8]) -> Result<String, String> {
145        println!("LocalStorage: Storing {} bytes to '{}'", data.len(), path);
146        Ok(format!("/local/{}", path))
147    }
148
149    fn retrieve(&self, path: &str) -> Result<Vec<u8>, String> {
150        println!("LocalStorage: Retrieving from '{}'", path);
151        Ok(b"local file content".to_vec())
152    }
153}
154
155/// S3 cloud storage
156#[derive(Default)]
157pub struct S3Storage;
158
159impl Storage for S3Storage {
160    fn store(&self, path: &str, data: &[u8]) -> Result<String, String> {
161        println!("S3Storage: Storing {} bytes to '{}'", data.len(), path);
162        Ok(format!("https://bucket.s3.amazonaws.com/{}", path))
163    }
164
165    fn retrieve(&self, path: &str) -> Result<Vec<u8>, String> {
166        println!("S3Storage: Retrieving from '{}'", path);
167        Ok(b"s3 file content".to_vec())
168    }
169}
170
171/// Example 1: Multiple implementations with environment-based selection
172pub fn example_environment_based_binding() -> Result<IocContainer, CoreError> {
173    let mut container = IocContainer::new();
174
175    // Redis cache for production environment
176    let redis_config = AdvancedBindingBuilder::<dyn Cache>::new()
177        .named("redis")
178        .when_env("CACHE_PROVIDER", "redis")
179        .with_lifetime(ServiceScope::Singleton)
180        .config();
181    container.with_implementation::<dyn Cache, RedisCache>(redis_config);
182
183    // Memory cache for development/test environment
184    let memory_config = AdvancedBindingBuilder::<dyn Cache>::new()
185        .named("memory")
186        .when_env("CACHE_PROVIDER", "memory")
187        .with_lifetime(ServiceScope::Singleton)
188        .config();
189    container.with_implementation::<dyn Cache, MemoryCache>(memory_config);
190
191    // Hybrid cache as default
192    let hybrid_config = AdvancedBindingBuilder::<dyn Cache>::new()
193        .as_default()
194        .with_lifetime(ServiceScope::Singleton)
195        .config();
196    container.with_implementation::<dyn Cache, HybridCache>(hybrid_config);
197
198    container.build()?;
199    Ok(container)
200}
201
202/// Example 2: Feature flag based service selection
203pub fn example_feature_flag_binding() -> Result<IocContainer, CoreError> {
204    let mut container = IocContainer::new();
205
206    // Cloud storage when cloud features are enabled
207    let s3_config = AdvancedBindingBuilder::<dyn Storage>::new()
208        .when_feature("cloud-storage")
209        .with_lifetime(ServiceScope::Singleton)
210        .config();
211    container.with_implementation::<dyn Storage, S3Storage>(s3_config);
212
213    // Local storage when cloud features are disabled
214    let local_config = AdvancedBindingBuilder::<dyn Storage>::new()
215        .when_not_feature("cloud-storage")
216        .with_lifetime(ServiceScope::Singleton)
217        .config();
218    container.with_implementation::<dyn Storage, LocalStorage>(local_config);
219
220    container.build()?;
221    Ok(container)
222}
223
224/// Example 3: Profile-based configuration
225pub fn example_profile_based_binding() -> Result<IocContainer, CoreError> {
226    let mut container = IocContainer::new();
227
228    // Production email service
229    let smtp_config = AdvancedBindingBuilder::<dyn EmailService>::new()
230        .named("production_email")
231        .in_profile("production")
232        .with_lifetime(ServiceScope::Singleton)
233        .config();
234    container.with_implementation::<dyn EmailService, SmtpEmailService>(smtp_config);
235
236    // Development email service
237    let sendgrid_config = AdvancedBindingBuilder::<dyn EmailService>::new()
238        .named("dev_email")
239        .in_profile("development")
240        .with_lifetime(ServiceScope::Singleton)
241        .config();
242    container.with_implementation::<dyn EmailService, SendGridEmailService>(sendgrid_config);
243
244    container.build()?;
245    Ok(container)
246}
247
248/// Example 4: Custom condition binding
249pub fn example_custom_condition_binding() -> Result<IocContainer, CoreError> {
250    let mut container = IocContainer::new();
251
252    // Use Redis cache if Redis URL is available
253    let redis_config = AdvancedBindingBuilder::<dyn Cache>::new()
254        .named("conditional_cache")
255        .when(|| std::env::var("REDIS_URL").is_ok())
256        .with_lifetime(ServiceScope::Singleton)
257        .config();
258    container.with_implementation::<dyn Cache, RedisCache>(redis_config);
259
260    // Fallback to memory cache
261    let memory_config = AdvancedBindingBuilder::<dyn Cache>::new()
262        .named("fallback_cache")
263        .when(|| std::env::var("REDIS_URL").is_err())
264        .with_lifetime(ServiceScope::Singleton)
265        .config();
266    container.with_implementation::<dyn Cache, MemoryCache>(memory_config);
267
268    container.build()?;
269    Ok(container)
270}
271
272/// Example 5: Factory patterns and lazy initialization
273pub fn example_factory_patterns() -> Result<IocContainer, CoreError> {
274    let mut container = IocContainer::new();
275
276    // Lazy-initialized expensive cache
277    container.bind_lazy::<RedisCache, _, _>(|| {
278        println!("Initializing expensive Redis connection...");
279        std::thread::sleep(std::time::Duration::from_millis(10)); // Simulate setup time
280        RedisCache::default()
281    });
282
283    // Factory with custom logic
284    container.bind_factory::<dyn Cache, _, _>(|| {
285        let cache_type = std::env::var("CACHE_TYPE").unwrap_or_else(|_| "memory".to_string());
286        match cache_type.as_str() {
287            "redis" => Ok(Box::new(RedisCache::default()) as Box<dyn Cache>),
288            "memory" => Ok(Box::new(MemoryCache::default()) as Box<dyn Cache>),
289            _ => Ok(Box::new(HybridCache) as Box<dyn Cache>),
290        }
291    });
292
293    container.build()?;
294    Ok(container)
295}
296
297/// Example 6: Collection bindings for plugin architecture
298pub fn example_collection_binding() -> Result<IocContainer, CoreError> {
299    let mut container = IocContainer::new();
300
301    // Register multiple cache implementations as a collection
302    container.bind_collection::<dyn Cache, _>(|collection| {
303        collection
304            .add::<RedisCache>()
305            .add::<MemoryCache>()
306            .add_named::<HybridCache>("hybrid");
307    });
308
309    // Register multiple storage providers
310    container.bind_collection::<dyn Storage, _>(|collection| {
311        collection.add::<LocalStorage>().add::<S3Storage>();
312    });
313
314    container.build()?;
315    Ok(container)
316}
317
318/// Example 7: Complex multi-condition binding
319pub fn example_complex_conditions() -> Result<IocContainer, CoreError> {
320    let mut container = IocContainer::new();
321
322    // Complex cache configuration for production with Redis
323    let production_redis_config = AdvancedBindingBuilder::<dyn Cache>::new()
324        .named("production_cache")
325        .in_profile("production")
326        .when_env("CACHE_PROVIDER", "redis")
327        .when_feature("high-performance")
328        .when(|| std::env::var("REDIS_CLUSTER_NODES").is_ok())
329        .with_lifetime(ServiceScope::Singleton)
330        .config();
331    container.with_implementation::<dyn Cache, RedisCache>(production_redis_config);
332
333    // Staging configuration
334    let staging_config = AdvancedBindingBuilder::<dyn Cache>::new()
335        .named("staging_cache")
336        .in_profile("staging")
337        .when_env("CACHE_PROVIDER", "hybrid")
338        .with_lifetime(ServiceScope::Singleton)
339        .config();
340    container.with_implementation::<dyn Cache, HybridCache>(staging_config);
341
342    // Development fallback
343    let dev_config = AdvancedBindingBuilder::<dyn Cache>::new()
344        .named("dev_cache")
345        .in_profile("development")
346        .config();
347    container.with_implementation::<dyn Cache, MemoryCache>(dev_config);
348
349    container.build()?;
350    Ok(container)
351}
352
353/// Example usage demonstration
354pub fn demonstrate_advanced_binding_features() -> Result<(), CoreError> {
355    println!("=== Advanced Binding Features Demo ===\n");
356
357    // Example 1: Environment-based binding
358    println!("1. Environment-based binding:");
359    std::env::set_var("CACHE_PROVIDER", "redis");
360    let container1 = example_environment_based_binding()?;
361    if let Ok(cache) = container1.resolve_named::<RedisCache>("redis") {
362        cache.set("test_key", "test_value".to_string()).ok();
363    }
364    std::env::remove_var("CACHE_PROVIDER");
365
366    // Example 2: Feature flag binding
367    println!("\n2. Feature flag binding:");
368    std::env::set_var("FEATURE_CLOUD-STORAGE", "1");
369    let container2 = example_feature_flag_binding()?;
370    if let Ok(storage) = container2.resolve::<S3Storage>() {
371        storage.store("test.txt", b"test data").ok();
372    }
373    std::env::remove_var("FEATURE_CLOUD-STORAGE");
374
375    // Example 3: Profile-based binding
376    println!("\n3. Profile-based binding:");
377    std::env::set_var("PROFILE", "production");
378    let container3 = example_profile_based_binding()?;
379    if let Ok(email) = container3.resolve_named::<SmtpEmailService>("production_email") {
380        email
381            .send_email("user@example.com", "Test", "Hello World")
382            .ok();
383    }
384    std::env::remove_var("PROFILE");
385
386    // Example 4: Factory patterns
387    println!("\n4. Factory patterns:");
388    let container4 = example_factory_patterns()?;
389    if let Ok(cache) = container4.resolve::<RedisCache>() {
390        cache.set("lazy_key", "lazy_value".to_string()).ok();
391    }
392
393    // Example 5: Service statistics
394    println!("\n5. Service statistics:");
395    let stats = container4.get_statistics();
396    println!("Total services: {}", stats.total_services);
397    println!("Singleton services: {}", stats.singleton_services);
398    println!("Cached instances: {}", stats.cached_instances);
399
400    // Example 6: Service validation
401    println!("\n6. Service validation:");
402    match container4.validate_all_services() {
403        Ok(()) => println!("All services are valid!"),
404        Err(errors) => println!("Validation errors: {}", errors.len()),
405    }
406
407    println!("\n=== Demo Complete ===");
408    Ok(())
409}
410
411#[cfg(test)]
412mod example_tests {
413    use super::*;
414    use serial_test::serial;
415
416    #[test]
417    #[serial]
418    fn test_environment_based_example() {
419        std::env::set_var("CACHE_PROVIDER", "memory");
420        let result = example_environment_based_binding();
421        assert!(result.is_ok());
422        std::env::remove_var("CACHE_PROVIDER");
423    }
424
425    #[test]
426    fn test_feature_flag_example() {
427        let result = example_feature_flag_binding();
428        assert!(result.is_ok());
429    }
430
431    #[test]
432    #[serial]
433    fn test_profile_based_example() {
434        std::env::set_var("PROFILE", "development");
435        let result = example_profile_based_binding();
436        assert!(result.is_ok());
437        std::env::remove_var("PROFILE");
438    }
439
440    #[test]
441    fn test_factory_patterns_example() {
442        let result = example_factory_patterns();
443        assert!(result.is_ok());
444    }
445
446    #[test]
447    #[serial]
448    fn test_demonstration() {
449        let result = demonstrate_advanced_binding_features();
450        assert!(result.is_ok());
451    }
452}