1use crate::container::autowiring::Injectable;
2use crate::container::descriptor::{ServiceDescriptor, ServiceDescriptorFactoryBuilder, ServiceId};
3use crate::container::scope::ServiceScope;
4use crate::errors::CoreError;
5
6pub type ConditionFn = Box<dyn Fn() -> bool + Send + Sync>;
8
9pub type EnvCondition = (&'static str, String);
11
12pub struct BindingConfig {
14 pub name: Option<String>,
16 pub lifetime: ServiceScope,
18 pub env_conditions: Vec<EnvCondition>,
20 pub feature_conditions: Vec<(String, bool)>,
22 pub conditions: Vec<ConditionFn>,
24 pub is_default: bool,
26 pub profile_conditions: Vec<String>,
28}
29
30impl Default for BindingConfig {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl BindingConfig {
37 pub fn new() -> Self {
38 Self {
39 name: None,
40 lifetime: ServiceScope::Transient,
41 env_conditions: Vec::new(),
42 feature_conditions: Vec::new(),
43 conditions: Vec::new(),
44 is_default: false,
45 profile_conditions: Vec::new(),
46 }
47 }
48
49 pub fn evaluate_conditions(&self) -> bool {
51 for (key, expected_value) in &self.env_conditions {
53 if let Ok(actual_value) = std::env::var(key) {
54 if actual_value != *expected_value {
55 return false;
56 }
57 } else {
58 return false;
59 }
60 }
61
62 for (feature, expected) in &self.feature_conditions {
64 let feature_enabled =
65 std::env::var(format!("FEATURE_{}", feature.to_uppercase())).is_ok();
66 if feature_enabled != *expected {
67 return false;
68 }
69 }
70
71 if !self.profile_conditions.is_empty() {
73 let current_profile =
74 std::env::var("PROFILE").unwrap_or_else(|_| "development".to_string());
75 if !self.profile_conditions.contains(¤t_profile) {
76 return false;
77 }
78 }
79
80 for condition in &self.conditions {
82 if !condition() {
83 return false;
84 }
85 }
86
87 true
88 }
89}
90
91pub struct AdvancedBindingBuilder<TInterface: ?Sized + 'static> {
93 config: BindingConfig,
94 _phantom: std::marker::PhantomData<*const TInterface>,
95}
96
97impl<TInterface: ?Sized + 'static> Default for AdvancedBindingBuilder<TInterface> {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103impl<TInterface: ?Sized + 'static> AdvancedBindingBuilder<TInterface> {
104 pub fn new() -> Self {
105 Self {
106 config: BindingConfig::new(),
107 _phantom: std::marker::PhantomData,
108 }
109 }
110
111 pub fn named(mut self, name: impl Into<String>) -> Self {
113 self.config.name = Some(name.into());
114 self
115 }
116
117 pub fn with_lifetime(mut self, lifetime: ServiceScope) -> Self {
119 self.config.lifetime = lifetime;
120 self
121 }
122
123 pub fn when_env(mut self, key: &'static str, value: impl Into<String>) -> Self {
125 self.config.env_conditions.push((key, value.into()));
126 self
127 }
128
129 pub fn when_feature(mut self, feature: impl Into<String>) -> Self {
131 self.config.feature_conditions.push((feature.into(), true));
132 self
133 }
134
135 pub fn when_not_feature(mut self, feature: impl Into<String>) -> Self {
137 self.config.feature_conditions.push((feature.into(), false));
138 self
139 }
140
141 pub fn when<F>(mut self, condition: F) -> Self
143 where
144 F: Fn() -> bool + Send + Sync + 'static,
145 {
146 self.config.conditions.push(Box::new(condition));
147 self
148 }
149
150 pub fn as_default(mut self) -> Self {
152 self.config.is_default = true;
153 self
154 }
155
156 pub fn in_profile(mut self, profile: impl Into<String>) -> Self {
158 self.config.profile_conditions.push(profile.into());
159 self
160 }
161
162 pub fn config(self) -> BindingConfig {
164 self.config
165 }
166}
167
168pub trait ServiceBinder {
170 fn add_service_descriptor(
172 &mut self,
173 descriptor: crate::container::descriptor::ServiceDescriptor,
174 ) -> Result<&mut Self, crate::errors::CoreError>;
175
176 fn bind<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
178 &mut self,
179 ) -> &mut Self;
180
181 fn bind_singleton<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
183 &mut self,
184 ) -> &mut Self;
185
186 fn bind_transient<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
188 &mut self,
189 ) -> &mut Self;
190
191 fn bind_factory<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
193 where
194 F: Fn() -> Result<T, CoreError> + Send + Sync + 'static,
195 T: Send + Sync + 'static;
196
197 fn bind_instance<TInterface: ?Sized + 'static, TImpl: Send + Sync + Clone + 'static>(
199 &mut self,
200 instance: TImpl,
201 ) -> &mut Self;
202
203 fn bind_named<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
205 &mut self,
206 name: &str,
207 ) -> &mut Self;
208
209 fn bind_injectable<T: Injectable>(&mut self) -> &mut Self;
211
212 fn bind_injectable_singleton<T: Injectable>(&mut self) -> &mut Self;
214
215 fn bind_with<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
219 &mut self,
220 ) -> AdvancedBindingBuilder<TInterface>;
221
222 fn with_implementation<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
224 &mut self,
225 config: BindingConfig,
226 ) -> &mut Self;
227
228 fn bind_lazy<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
230 where
231 F: Fn() -> T + Send + Sync + 'static,
232 T: Send + Sync + 'static;
233
234 fn bind_parameterized_factory<TInterface: ?Sized + 'static, P, F, T>(
236 &mut self,
237 factory: F,
238 ) -> &mut Self
239 where
240 F: Fn(P) -> Result<T, CoreError> + Send + Sync + 'static,
241 T: Send + Sync + 'static,
242 P: Send + Sync + 'static;
243
244 fn bind_collection<TInterface: ?Sized + 'static, F>(&mut self, configure: F) -> &mut Self
246 where
247 F: FnOnce(&mut CollectionBindingBuilder<TInterface>);
248}
249
250pub struct CollectionBindingBuilder<TInterface: ?Sized + 'static> {
252 services: Vec<ServiceDescriptor>,
253 _phantom: std::marker::PhantomData<*const TInterface>,
254}
255
256impl<TInterface: ?Sized + 'static> Default for CollectionBindingBuilder<TInterface> {
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262impl<TInterface: ?Sized + 'static> CollectionBindingBuilder<TInterface> {
263 pub fn new() -> Self {
264 Self {
265 services: Vec::new(),
266 _phantom: std::marker::PhantomData,
267 }
268 }
269
270 pub fn add<TImpl: Send + Sync + Default + 'static>(&mut self) -> &mut Self {
272 let descriptor = ServiceDescriptor::bind::<TInterface, TImpl>()
273 .with_lifetime(ServiceScope::Transient)
274 .build();
275 self.services.push(descriptor);
276 self
277 }
278
279 pub fn add_named<TImpl: Send + Sync + Default + 'static>(
281 &mut self,
282 name: impl Into<String>,
283 ) -> &mut Self {
284 let descriptor = ServiceDescriptor::bind_named::<TInterface, TImpl>(name)
285 .with_lifetime(ServiceScope::Transient)
286 .build();
287 self.services.push(descriptor);
288 self
289 }
290
291 pub fn add_singleton<TImpl: Send + Sync + Default + 'static>(&mut self) -> &mut Self {
293 let descriptor = ServiceDescriptor::bind::<TInterface, TImpl>()
294 .with_lifetime(ServiceScope::Singleton)
295 .build();
296 self.services.push(descriptor);
297 self
298 }
299
300 pub fn add_named_singleton<TImpl: Send + Sync + Default + 'static>(
302 &mut self,
303 name: impl Into<String>,
304 ) -> &mut Self {
305 let descriptor = ServiceDescriptor::bind_named::<TInterface, TImpl>(name)
306 .with_lifetime(ServiceScope::Singleton)
307 .build();
308 self.services.push(descriptor);
309 self
310 }
311
312 pub(crate) fn into_services(self) -> Vec<ServiceDescriptor> {
314 self.services
315 }
316}
317
318#[derive(Debug)]
320pub struct ServiceBindings {
321 descriptors: Vec<ServiceDescriptor>,
322}
323
324impl ServiceBindings {
325 pub fn new() -> Self {
327 Self {
328 descriptors: Vec::new(),
329 }
330 }
331
332 pub fn add_descriptor(&mut self, descriptor: ServiceDescriptor) {
334 self.descriptors.push(descriptor);
335 }
336
337 pub fn descriptors(&self) -> &[ServiceDescriptor] {
339 &self.descriptors
340 }
341
342 pub fn get_descriptor(&self, service_id: &ServiceId) -> Option<&ServiceDescriptor> {
344 self.descriptors
345 .iter()
346 .find(|d| d.service_id == *service_id)
347 }
348
349 pub fn get_descriptor_named<T: 'static + ?Sized>(
351 &self,
352 name: &str,
353 ) -> Option<&ServiceDescriptor> {
354 self.descriptors
355 .iter()
356 .find(|d| d.service_id.matches_named::<T>(name))
357 }
358
359 pub fn service_ids(&self) -> Vec<ServiceId> {
361 self.descriptors
362 .iter()
363 .map(|d| d.service_id.clone())
364 .collect()
365 }
366
367 pub fn contains(&self, service_id: &ServiceId) -> bool {
369 self.descriptors.iter().any(|d| d.service_id == *service_id)
370 }
371
372 pub fn contains_named<T: 'static + ?Sized>(&self, name: &str) -> bool {
374 self.descriptors
375 .iter()
376 .any(|d| d.service_id.matches_named::<T>(name))
377 }
378
379 pub fn count(&self) -> usize {
381 self.descriptors.len()
382 }
383
384 pub fn into_descriptors(self) -> Vec<ServiceDescriptor> {
386 self.descriptors
387 }
388}
389
390impl Default for ServiceBindings {
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396impl ServiceBinder for ServiceBindings {
397 fn add_service_descriptor(
398 &mut self,
399 descriptor: crate::container::descriptor::ServiceDescriptor,
400 ) -> Result<&mut Self, crate::errors::CoreError> {
401 self.add_descriptor(descriptor);
402 Ok(self)
403 }
404
405 fn bind<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
406 &mut self,
407 ) -> &mut Self {
408 let descriptor = ServiceDescriptor::bind::<TInterface, TImpl>()
409 .with_lifetime(ServiceScope::Transient)
410 .build();
411 self.add_descriptor(descriptor);
412 self
413 }
414
415 fn bind_singleton<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
416 &mut self,
417 ) -> &mut Self {
418 let descriptor = ServiceDescriptor::bind::<TInterface, TImpl>()
419 .with_lifetime(ServiceScope::Singleton)
420 .build();
421 self.add_descriptor(descriptor);
422 self
423 }
424
425 fn bind_transient<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
426 &mut self,
427 ) -> &mut Self {
428 let descriptor = ServiceDescriptor::bind::<TInterface, TImpl>()
429 .with_lifetime(ServiceScope::Transient)
430 .build();
431 self.add_descriptor(descriptor);
432 self
433 }
434
435 fn bind_factory<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
436 where
437 F: Fn() -> Result<T, CoreError> + Send + Sync + 'static,
438 T: Send + Sync + 'static,
439 {
440 let descriptor = ServiceDescriptorFactoryBuilder::<TInterface>::new()
441 .with_factory(factory)
442 .build()
443 .expect("Failed to build factory descriptor");
444 self.add_descriptor(descriptor);
445 self
446 }
447
448 fn bind_instance<TInterface: ?Sized + 'static, TImpl: Send + Sync + Clone + 'static>(
449 &mut self,
450 instance: TImpl,
451 ) -> &mut Self {
452 let descriptor = ServiceDescriptorFactoryBuilder::<TInterface>::new()
453 .with_lifetime(ServiceScope::Singleton)
454 .with_factory({
455 let instance = instance.clone();
456 move || -> Result<TImpl, CoreError> { Ok(instance.clone()) }
457 })
458 .build()
459 .expect("Failed to build instance descriptor");
460 self.add_descriptor(descriptor);
461 self
462 }
463
464 fn bind_named<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
465 &mut self,
466 name: &str,
467 ) -> &mut Self {
468 let descriptor = ServiceDescriptor::bind_named::<TInterface, TImpl>(name)
469 .with_lifetime(ServiceScope::Transient)
470 .build();
471 self.add_descriptor(descriptor);
472 self
473 }
474
475 fn bind_injectable<T: Injectable>(&mut self) -> &mut Self {
476 let dependencies = T::dependencies();
477 let descriptor = ServiceDescriptor::autowired::<T>(dependencies);
478 self.add_descriptor(descriptor);
479 self
480 }
481
482 fn bind_injectable_singleton<T: Injectable>(&mut self) -> &mut Self {
483 let dependencies = T::dependencies();
484 let descriptor = ServiceDescriptor::autowired_singleton::<T>(dependencies);
485 self.add_descriptor(descriptor);
486 self
487 }
488
489 fn bind_with<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
492 &mut self,
493 ) -> AdvancedBindingBuilder<TInterface> {
494 AdvancedBindingBuilder::new()
495 }
496
497 fn with_implementation<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
498 &mut self,
499 config: BindingConfig,
500 ) -> &mut Self {
501 if config.evaluate_conditions() {
503 let mut builder = if let Some(name) = &config.name {
504 ServiceDescriptor::bind_named::<TInterface, TImpl>(name.clone())
505 } else {
506 ServiceDescriptor::bind::<TInterface, TImpl>()
507 };
508
509 builder = builder.with_lifetime(config.lifetime);
510 let descriptor = builder.build();
511 self.add_descriptor(descriptor);
512 }
513 self
514 }
515
516 fn bind_lazy<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
517 where
518 F: Fn() -> T + Send + Sync + 'static,
519 T: Send + Sync + 'static,
520 {
521 let lazy_factory = move || -> Result<T, CoreError> { Ok(factory()) };
522
523 let descriptor = ServiceDescriptorFactoryBuilder::<TInterface>::new()
524 .with_factory(lazy_factory)
525 .build()
526 .expect("Failed to build lazy factory descriptor");
527 self.add_descriptor(descriptor);
528 self
529 }
530
531 fn bind_parameterized_factory<TInterface: ?Sized + 'static, P, F, T>(
532 &mut self,
533 _factory: F,
534 ) -> &mut Self
535 where
536 F: Fn(P) -> Result<T, CoreError> + Send + Sync + 'static,
537 T: Send + Sync + 'static,
538 P: Send + Sync + 'static,
539 {
540 let descriptor = ServiceDescriptorFactoryBuilder::<TInterface>::new()
543 .with_factory(move || -> Result<T, CoreError> {
544 Err(CoreError::ServiceNotFound {
547 service_type: format!(
548 "Parameterized factory for {} requires runtime parameter resolution",
549 std::any::type_name::<TInterface>()
550 ),
551 })
552 })
553 .build()
554 .expect("Failed to build parameterized factory descriptor");
555 self.add_descriptor(descriptor);
556 self
557 }
558
559 fn bind_collection<TInterface: ?Sized + 'static, F>(&mut self, configure: F) -> &mut Self
560 where
561 F: FnOnce(&mut CollectionBindingBuilder<TInterface>),
562 {
563 let mut builder = CollectionBindingBuilder::new();
564 configure(&mut builder);
565 let services = builder.into_services();
566 for service in services {
567 self.add_descriptor(service);
568 }
569 self
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576 use serial_test::serial;
577
578 #[allow(dead_code)]
579 trait TestRepository: Send + Sync {
580 fn find(&self, id: u32) -> Option<String>;
581 }
582
583 #[derive(Default)]
584 struct PostgresRepository;
585
586 unsafe impl Send for PostgresRepository {}
587 unsafe impl Sync for PostgresRepository {}
588
589 impl TestRepository for PostgresRepository {
590 fn find(&self, _id: u32) -> Option<String> {
591 Some("postgres".to_string())
592 }
593 }
594
595 #[allow(dead_code)]
596 trait TestService: Send + Sync {
597 fn get_data(&self) -> String;
598 }
599
600 #[derive(Default)]
601 struct UserService;
602
603 unsafe impl Send for UserService {}
604 unsafe impl Sync for UserService {}
605
606 impl TestService for UserService {
607 fn get_data(&self) -> String {
608 "user_data".to_string()
609 }
610 }
611
612 #[test]
613 fn test_service_bindings() {
614 let mut bindings = ServiceBindings::new();
615
616 bindings
617 .bind::<PostgresRepository, PostgresRepository>()
618 .bind_singleton::<UserService, UserService>()
619 .bind_named::<PostgresRepository, PostgresRepository>("postgres");
620
621 assert_eq!(bindings.count(), 3);
622
623 let service_ids = bindings.service_ids();
624 assert_eq!(service_ids.len(), 3);
625
626 assert!(bindings.contains(&ServiceId::of::<PostgresRepository>()));
628 assert!(bindings.contains(&ServiceId::of::<UserService>()));
629 assert!(bindings.contains(&ServiceId::named::<PostgresRepository>("postgres")));
630 }
631
632 #[test]
633 fn test_factory_binding() {
634 let mut bindings = ServiceBindings::new();
635
636 bindings.bind_factory::<UserService, _, _>(|| Ok(UserService::default()));
637
638 assert_eq!(bindings.count(), 1);
639 assert!(bindings.contains(&ServiceId::of::<UserService>()));
640 }
641
642 #[test]
643 #[serial]
644 fn test_advanced_binding_with_environment_conditions() {
645 let mut bindings = ServiceBindings::new();
646
647 std::env::set_var("CACHE_PROVIDER", "redis");
649
650 let config = AdvancedBindingBuilder::<dyn TestRepository>::new()
651 .named("redis")
652 .when_env("CACHE_PROVIDER", "redis")
653 .with_lifetime(ServiceScope::Singleton)
654 .config();
655
656 bindings.with_implementation::<dyn TestRepository, PostgresRepository>(config);
657
658 assert_eq!(bindings.count(), 1);
659 assert!(bindings.contains_named::<dyn TestRepository>("redis"));
660
661 std::env::remove_var("CACHE_PROVIDER");
663 }
664
665 #[test]
666 fn test_conditional_binding_not_met() {
667 let mut bindings = ServiceBindings::new();
668
669 let config = AdvancedBindingBuilder::<dyn TestRepository>::new()
671 .named("nonexistent")
672 .when_env("NON_EXISTENT_VAR", "value")
673 .config();
674
675 bindings.with_implementation::<dyn TestRepository, PostgresRepository>(config);
676
677 assert_eq!(bindings.count(), 0);
679 }
680
681 #[test]
682 #[serial]
683 fn test_feature_flag_conditions() {
684 let mut bindings = ServiceBindings::new();
685
686 std::env::set_var("FEATURE_ADVANCED_CACHE", "1");
688
689 let config = AdvancedBindingBuilder::<dyn TestRepository>::new()
690 .when_feature("advanced_cache")
691 .config();
692
693 bindings.with_implementation::<dyn TestRepository, PostgresRepository>(config);
694
695 assert_eq!(bindings.count(), 1);
696
697 std::env::remove_var("FEATURE_ADVANCED_CACHE");
699 }
700
701 #[test]
702 #[serial]
703 fn test_profile_conditions() {
704 let mut bindings = ServiceBindings::new();
705
706 std::env::set_var("PROFILE", "development");
708
709 let config = AdvancedBindingBuilder::<dyn TestService>::new()
710 .in_profile("development")
711 .config();
712
713 bindings.with_implementation::<dyn TestService, UserService>(config);
714
715 assert_eq!(bindings.count(), 1);
716
717 std::env::set_var("PROFILE", "production");
719
720 let config2 = AdvancedBindingBuilder::<dyn TestRepository>::new()
721 .in_profile("development")
722 .config();
723
724 bindings.with_implementation::<dyn TestRepository, PostgresRepository>(config2);
725
726 assert_eq!(bindings.count(), 1);
728
729 std::env::remove_var("PROFILE");
731 }
732
733 #[test]
734 fn test_custom_conditions() {
735 let mut bindings = ServiceBindings::new();
736
737 let config = AdvancedBindingBuilder::<dyn TestService>::new()
738 .when(|| true) .config();
740
741 bindings.with_implementation::<dyn TestService, UserService>(config);
742
743 assert_eq!(bindings.count(), 1);
744
745 let config2 = AdvancedBindingBuilder::<dyn TestRepository>::new()
746 .when(|| false) .config();
748
749 bindings.with_implementation::<dyn TestRepository, PostgresRepository>(config2);
750
751 assert_eq!(bindings.count(), 1);
753 }
754
755 #[test]
756 fn test_lazy_binding() {
757 let mut bindings = ServiceBindings::new();
758
759 bindings.bind_lazy::<UserService, _, _>(|| UserService::default());
760
761 assert_eq!(bindings.count(), 1);
762 assert!(bindings.contains(&ServiceId::of::<UserService>()));
763 }
764
765 #[test]
766 fn test_collection_binding() {
767 let mut bindings = ServiceBindings::new();
768
769 bindings.bind_collection::<dyn TestService, _>(|collection| {
771 collection
772 .add::<UserService>()
773 .add_named::<UserService>("named_user_service");
774 });
775
776 assert_eq!(bindings.count(), 2);
778 assert!(bindings.contains(&ServiceId::of::<dyn TestService>()));
779 assert!(bindings.contains(&ServiceId::named::<dyn TestService>("named_user_service")));
780 }
781
782 #[test]
783 #[serial]
784 fn test_multiple_conditions() {
785 let mut bindings = ServiceBindings::new();
786
787 std::env::set_var("ENV_VAR", "test_value");
789 std::env::set_var("FEATURE_TEST", "1");
790 std::env::set_var("PROFILE", "test");
791
792 let config = AdvancedBindingBuilder::<dyn TestService>::new()
793 .when_env("ENV_VAR", "test_value")
794 .when_feature("test")
795 .in_profile("test")
796 .when(|| true)
797 .named("complex_service")
798 .with_lifetime(ServiceScope::Singleton)
799 .config();
800
801 bindings.with_implementation::<dyn TestService, UserService>(config);
802
803 assert_eq!(bindings.count(), 1);
804 assert!(bindings.contains_named::<dyn TestService>("complex_service"));
805
806 std::env::remove_var("ENV_VAR");
808 std::env::remove_var("FEATURE_TEST");
809 std::env::remove_var("PROFILE");
810 }
811}