elif_core/container/
ioc_container.rs

1use std::any::Any;
2use std::collections::HashMap;
3use std::sync::{Arc, RwLock};
4
5use crate::container::autowiring::{DependencyResolver, Injectable};
6use crate::container::binding::{ServiceBinder, ServiceBindings};
7use crate::container::descriptor::ServiceId;
8use crate::container::lifecycle::ServiceLifecycleManager;
9use crate::container::resolver::DependencyResolver as GraphDependencyResolver;
10use crate::container::scope::{ScopeId, ScopedServiceManager, ServiceScope};
11use crate::container::tokens::{ServiceToken, TokenRegistry};
12use crate::errors::CoreError;
13
14/// Service instance storage
15#[derive(Debug)]
16enum ServiceInstance {
17    /// Singleton instance
18    Singleton(Arc<dyn Any + Send + Sync>),
19    /// Scoped instances by scope ID
20    Scoped(HashMap<ScopeId, Arc<dyn Any + Send + Sync>>),
21}
22
23/// Modern IoC container with proper dependency injection
24#[derive(Debug)]
25pub struct IocContainer {
26    /// Service bindings and descriptors
27    bindings: ServiceBindings,
28    /// Token-based service registry
29    tokens: TokenRegistry,
30    /// Dependency resolver
31    resolver: Option<GraphDependencyResolver>,
32    /// Instantiated services
33    instances: Arc<RwLock<HashMap<ServiceId, ServiceInstance>>>,
34    /// Service lifecycle manager
35    lifecycle_manager: ServiceLifecycleManager,
36    /// Active scopes
37    scopes: Arc<RwLock<HashMap<ScopeId, Arc<ScopedServiceManager>>>>,
38    /// Whether the container is built and ready
39    is_built: bool,
40}
41
42impl IocContainer {
43    /// Create a new IoC container
44    pub fn new() -> Self {
45        Self {
46            bindings: ServiceBindings::new(),
47            tokens: TokenRegistry::new(),
48            resolver: None,
49            instances: Arc::new(RwLock::new(HashMap::new())),
50            lifecycle_manager: ServiceLifecycleManager::new(),
51            scopes: Arc::new(RwLock::new(HashMap::new())),
52            is_built: false,
53        }
54    }
55
56    /// Create IoC container from existing bindings
57    pub fn from_bindings(bindings: ServiceBindings) -> Self {
58        Self {
59            bindings,
60            tokens: TokenRegistry::new(),
61            resolver: None,
62            instances: Arc::new(RwLock::new(HashMap::new())),
63            lifecycle_manager: ServiceLifecycleManager::new(),
64            scopes: Arc::new(RwLock::new(HashMap::new())),
65            is_built: false,
66        }
67    }
68
69    /// Build the container and prepare for service resolution
70    pub fn build(&mut self) -> Result<(), CoreError> {
71        if self.is_built {
72            return Ok(());
73        }
74
75        // Build dependency resolver
76        let resolver = GraphDependencyResolver::new(self.bindings.descriptors())?;
77        self.resolver = Some(resolver);
78
79        // Validate dependencies
80        let service_ids = self.bindings.service_ids().into_iter().collect();
81        if let Some(resolver) = &self.resolver {
82            resolver.validate_dependencies(&service_ids)?;
83        }
84
85        self.is_built = true;
86        Ok(())
87    }
88
89    /// Initialize all async services
90    pub async fn initialize_async(&mut self) -> Result<(), CoreError> {
91        self.lifecycle_manager.initialize_all().await
92    }
93
94    /// Initialize all async services with timeout
95    pub async fn initialize_async_with_timeout(
96        &mut self,
97        timeout: std::time::Duration,
98    ) -> Result<(), CoreError> {
99        self.lifecycle_manager
100            .initialize_all_with_timeout(timeout)
101            .await
102    }
103
104    /// Create a new service scope
105    pub fn create_scope(&self) -> Result<ScopeId, CoreError> {
106        let scope_manager = Arc::new(ScopedServiceManager::new());
107        let scope_id = scope_manager.scope_id().clone();
108
109        let mut scopes = self.scopes.write().map_err(|_| CoreError::LockError {
110            resource: "scopes".to_string(),
111        })?;
112
113        scopes.insert(scope_id.clone(), scope_manager);
114        Ok(scope_id)
115    }
116
117    /// Create a child scope from an existing scope
118    pub fn create_child_scope(&self, parent_scope_id: &ScopeId) -> Result<ScopeId, CoreError> {
119        let mut scopes = self.scopes.write().map_err(|_| CoreError::LockError {
120            resource: "scopes".to_string(),
121        })?;
122
123        let parent_scope = scopes
124            .get(parent_scope_id)
125            .ok_or_else(|| CoreError::ServiceNotFound {
126                service_type: format!("parent scope {}", parent_scope_id),
127            })?
128            .clone(); // Clone the Arc, not the ScopedServiceManager
129
130        let child_scope = Arc::new(ScopedServiceManager::create_child(parent_scope));
131        let child_scope_id = child_scope.scope_id().clone();
132
133        scopes.insert(child_scope_id.clone(), child_scope);
134        Ok(child_scope_id)
135    }
136
137    /// Dispose of a scope and all its services
138    pub async fn dispose_scope(&self, scope_id: &ScopeId) -> Result<(), CoreError> {
139        let was_removed = {
140            let mut scopes = self.scopes.write().map_err(|_| CoreError::LockError {
141                resource: "scopes".to_string(),
142            })?;
143            scopes.remove(scope_id).is_some()
144        };
145
146        if was_removed {
147            // Remove scoped instances for this scope
148            let mut instances = self.instances.write().map_err(|_| CoreError::LockError {
149                resource: "service_instances".to_string(),
150            })?;
151
152            for (_, instance) in instances.iter_mut() {
153                if let ServiceInstance::Scoped(scoped_instances) = instance {
154                    scoped_instances.remove(scope_id);
155                }
156            }
157        }
158
159        Ok(())
160    }
161
162    /// Dispose all scoped services and lifecycle managed services
163    pub async fn dispose_all(&mut self) -> Result<(), CoreError> {
164        // Dispose all scoped services first
165        let scope_ids: Vec<ScopeId> = {
166            let scopes = self.scopes.read().map_err(|_| CoreError::LockError {
167                resource: "scopes".to_string(),
168            })?;
169            scopes.keys().cloned().collect()
170        };
171
172        for scope_id in scope_ids {
173            self.dispose_scope(&scope_id).await?;
174        }
175
176        // Dispose lifecycle managed services
177        self.lifecycle_manager.dispose_all().await?;
178
179        Ok(())
180    }
181
182    /// Get a reference to the lifecycle manager
183    pub fn lifecycle_manager(&self) -> &ServiceLifecycleManager {
184        &self.lifecycle_manager
185    }
186
187    /// Get a mutable reference to the lifecycle manager
188    pub fn lifecycle_manager_mut(&mut self) -> &mut ServiceLifecycleManager {
189        &mut self.lifecycle_manager
190    }
191
192    /// Resolve a service by type
193    pub fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError> {
194        let service_id = ServiceId::of::<T>();
195        self.resolve_by_id(&service_id)
196    }
197
198    /// Resolve a scoped service by type
199    pub fn resolve_scoped<T: Send + Sync + 'static>(
200        &self,
201        scope_id: &ScopeId,
202    ) -> Result<Arc<T>, CoreError> {
203        let service_id = ServiceId::of::<T>();
204        self.resolve_by_id_scoped(&service_id, scope_id)
205    }
206
207    /// Resolve a named service
208    pub fn resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Result<Arc<T>, CoreError> {
209        self.resolve_named_by_str::<T>(name)
210    }
211
212    /// Resolve a named service efficiently without allocating ServiceId
213    fn resolve_named_by_str<T: Send + Sync + 'static>(
214        &self,
215        name: &str,
216    ) -> Result<Arc<T>, CoreError> {
217        if !self.is_built {
218            return Err(CoreError::InvalidServiceDescriptor {
219                message: "Container must be built before resolving services".to_string(),
220            });
221        }
222
223        // Check if we have a cached instance - we need to create ServiceId for lookup in instances
224        let service_id = ServiceId::named::<T>(name.to_string());
225        {
226            let instances = self.instances.read().map_err(|_| CoreError::LockError {
227                resource: "service_instances".to_string(),
228            })?;
229
230            if let Some(ServiceInstance::Singleton(instance)) = instances.get(&service_id) {
231                return instance
232                    .clone()
233                    .downcast::<T>()
234                    .map_err(|_| CoreError::ServiceNotFound {
235                        service_type: format!("{}({})", std::any::type_name::<T>(), name),
236                    });
237            }
238        }
239
240        // Get service descriptor efficiently without allocating ServiceId
241        let descriptor = self
242            .bindings
243            .get_descriptor_named::<T>(name)
244            .ok_or_else(|| CoreError::ServiceNotFound {
245                service_type: format!("{}({})", std::any::type_name::<T>(), name),
246            })?;
247
248        // Resolve dependencies first
249        self.resolve_dependencies(&descriptor.dependencies)?;
250
251        // Create the service instance based on activation strategy
252        let arc_instance = match &descriptor.activation_strategy {
253            crate::container::descriptor::ServiceActivationStrategy::Factory(factory) => {
254                let instance = factory()?;
255                let typed_instance =
256                    instance
257                        .downcast::<T>()
258                        .map_err(|_| CoreError::ServiceNotFound {
259                            service_type: format!("{}({})", std::any::type_name::<T>(), name),
260                        })?;
261                Arc::new(*typed_instance)
262            }
263            crate::container::descriptor::ServiceActivationStrategy::AutoWired => {
264                return Err(CoreError::InvalidServiceDescriptor {
265                    message: format!(
266                        "Service {}({}) is marked as auto-wired but resolve_named was called instead of resolve_injectable. Use resolve_injectable() for auto-wired services.",
267                        std::any::type_name::<T>(),
268                        name
269                    ),
270                });
271            }
272        };
273
274        // Cache if singleton (we already have the ServiceId)
275        if descriptor.lifetime == ServiceScope::Singleton {
276            let mut instances = self.instances.write().map_err(|_| CoreError::LockError {
277                resource: "service_instances".to_string(),
278            })?;
279            instances.insert(service_id, ServiceInstance::Singleton(arc_instance.clone()));
280        }
281
282        Ok(arc_instance)
283    }
284
285    /// Resolve a service by service ID
286    fn resolve_by_id<T: Send + Sync + 'static>(
287        &self,
288        service_id: &ServiceId,
289    ) -> Result<Arc<T>, CoreError> {
290        if !self.is_built {
291            return Err(CoreError::InvalidServiceDescriptor {
292                message: "Container must be built before resolving services".to_string(),
293            });
294        }
295
296        // Check if we have a cached instance
297        {
298            let instances = self.instances.read().map_err(|_| CoreError::LockError {
299                resource: "service_instances".to_string(),
300            })?;
301
302            if let Some(ServiceInstance::Singleton(instance)) = instances.get(service_id) {
303                return instance
304                    .clone()
305                    .downcast::<T>()
306                    .map_err(|_| CoreError::ServiceNotFound {
307                        service_type: format!(
308                            "{}({})",
309                            std::any::type_name::<T>(),
310                            service_id.name.as_deref().unwrap_or("default")
311                        ),
312                    });
313            }
314        }
315
316        // Get service descriptor
317        let descriptor =
318            self.bindings
319                .get_descriptor(service_id)
320                .ok_or_else(|| CoreError::ServiceNotFound {
321                    service_type: format!(
322                        "{}({})",
323                        std::any::type_name::<T>(),
324                        service_id.name.as_deref().unwrap_or("default")
325                    ),
326                })?;
327
328        // Resolve dependencies first
329        self.resolve_dependencies(&descriptor.dependencies)?;
330
331        // Create the service instance based on activation strategy
332        let arc_instance = match &descriptor.activation_strategy {
333            crate::container::descriptor::ServiceActivationStrategy::Factory(factory) => {
334                let instance = factory()?;
335                let typed_instance =
336                    instance
337                        .downcast::<T>()
338                        .map_err(|_| CoreError::ServiceNotFound {
339                            service_type: format!(
340                                "{}({})",
341                                std::any::type_name::<T>(),
342                                service_id.name.as_deref().unwrap_or("default")
343                            ),
344                        })?;
345                Arc::new(*typed_instance)
346            }
347            crate::container::descriptor::ServiceActivationStrategy::AutoWired => {
348                return Err(CoreError::InvalidServiceDescriptor {
349                    message: format!(
350                        "Service {} is marked as auto-wired but resolve_by_id was called instead of resolve_injectable. Use resolve_injectable() for auto-wired services.",
351                        std::any::type_name::<T>()
352                    ),
353                });
354            }
355        };
356
357        // Cache if singleton
358        if descriptor.lifetime == ServiceScope::Singleton {
359            let mut instances = self.instances.write().map_err(|_| CoreError::LockError {
360                resource: "service_instances".to_string(),
361            })?;
362            instances.insert(
363                service_id.clone(),
364                ServiceInstance::Singleton(arc_instance.clone()),
365            );
366        }
367
368        Ok(arc_instance)
369    }
370
371    /// Resolve a service by service ID in a specific scope
372    fn resolve_by_id_scoped<T: Send + Sync + 'static>(
373        &self,
374        service_id: &ServiceId,
375        scope_id: &ScopeId,
376    ) -> Result<Arc<T>, CoreError> {
377        if !self.is_built {
378            return Err(CoreError::InvalidServiceDescriptor {
379                message: "Container must be built before resolving services".to_string(),
380            });
381        }
382
383        // Get service descriptor first to check lifetime
384        let descriptor =
385            self.bindings
386                .get_descriptor(service_id)
387                .ok_or_else(|| CoreError::ServiceNotFound {
388                    service_type: format!(
389                        "{}({})",
390                        std::any::type_name::<T>(),
391                        service_id.name.as_deref().unwrap_or("default")
392                    ),
393                })?;
394
395        // Handle based on lifetime
396        match descriptor.lifetime {
397            ServiceScope::Singleton => {
398                // For singleton, ignore scope and use regular resolution
399                self.resolve_by_id(service_id)
400            }
401            ServiceScope::Transient => {
402                // For transient, create new instance every time
403                self.create_service_instance::<T>(service_id, descriptor)
404            }
405            ServiceScope::Scoped => {
406                // Check if we have a cached instance for this scope
407                {
408                    let instances = self.instances.read().map_err(|_| CoreError::LockError {
409                        resource: "service_instances".to_string(),
410                    })?;
411
412                    if let Some(ServiceInstance::Scoped(scoped_instances)) =
413                        instances.get(service_id)
414                    {
415                        if let Some(instance) = scoped_instances.get(scope_id) {
416                            return instance.clone().downcast::<T>().map_err(|_| {
417                                CoreError::ServiceNotFound {
418                                    service_type: format!(
419                                        "{}({})",
420                                        std::any::type_name::<T>(),
421                                        service_id.name.as_deref().unwrap_or("default")
422                                    ),
423                                }
424                            });
425                        }
426                    }
427                }
428
429                // Create new scoped instance
430                let arc_instance = self.create_service_instance::<T>(service_id, descriptor)?;
431
432                // Cache it for this scope
433                let mut instances = self.instances.write().map_err(|_| CoreError::LockError {
434                    resource: "service_instances".to_string(),
435                })?;
436
437                use std::collections::hash_map::Entry;
438                match instances.entry(service_id.clone()) {
439                    Entry::Occupied(mut entry) => match entry.get_mut() {
440                        ServiceInstance::Scoped(scoped_instances) => {
441                            scoped_instances.insert(
442                                scope_id.clone(),
443                                arc_instance.clone() as Arc<dyn Any + Send + Sync>,
444                            );
445                        }
446                        ServiceInstance::Singleton(_) => {
447                            return Err(CoreError::InvalidServiceDescriptor {
448                                    message: format!(
449                                        "Service {} is registered as both Singleton and Scoped. This is a configuration error.",
450                                        std::any::type_name::<T>()
451                                    ),
452                                });
453                        }
454                    },
455                    Entry::Vacant(entry) => {
456                        let mut scoped_map = HashMap::new();
457                        scoped_map.insert(
458                            scope_id.clone(),
459                            arc_instance.clone() as Arc<dyn Any + Send + Sync>,
460                        );
461                        entry.insert(ServiceInstance::Scoped(scoped_map));
462                    }
463                }
464
465                Ok(arc_instance)
466            }
467        }
468    }
469
470    /// Create a service instance
471    fn create_service_instance<T: Send + Sync + 'static>(
472        &self,
473        service_id: &ServiceId,
474        descriptor: &crate::container::descriptor::ServiceDescriptor,
475    ) -> Result<Arc<T>, CoreError> {
476        // Resolve dependencies first
477        self.resolve_dependencies(&descriptor.dependencies)?;
478
479        // Create the service instance based on activation strategy
480        match &descriptor.activation_strategy {
481            crate::container::descriptor::ServiceActivationStrategy::Factory(factory) => {
482                let instance = factory()?;
483                let typed_instance = instance.downcast::<T>()
484                    .map_err(|_| CoreError::ServiceNotFound {
485                        service_type: format!("{}({})", 
486                            std::any::type_name::<T>(),
487                            service_id.name.as_deref().unwrap_or("default")
488                        ),
489                    })?;
490                Ok(Arc::new(*typed_instance))
491            },
492            crate::container::descriptor::ServiceActivationStrategy::AutoWired => {
493                Err(CoreError::InvalidServiceDescriptor {
494                    message: format!(
495                        "Service {} is marked as auto-wired but create_service_instance was called. Use resolve_injectable() for auto-wired services.",
496                        std::any::type_name::<T>()
497                    ),
498                })
499            }
500        }
501    }
502
503    /// Resolve all dependencies for a service
504    fn resolve_dependencies(&self, dependencies: &[ServiceId]) -> Result<(), CoreError> {
505        for dep_id in dependencies {
506            // For now, we'll just validate that the dependency exists
507            if !self.bindings.contains(dep_id) {
508                return Err(CoreError::ServiceNotFound {
509                    service_type: format!(
510                        "{}({})",
511                        dep_id.type_name(),
512                        dep_id.name.as_deref().unwrap_or("default")
513                    ),
514                });
515            }
516        }
517        Ok(())
518    }
519
520    /// Try to resolve a service, returning None if not found
521    pub fn try_resolve<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
522        self.resolve::<T>().ok()
523    }
524
525    /// Try to resolve a named service, returning None if not found
526    pub fn try_resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>> {
527        self.resolve_named::<T>(name).ok()
528    }
529
530    /// Resolve a service using the Injectable trait (auto-wiring)
531    pub fn resolve_injectable<T: Injectable>(&self) -> Result<Arc<T>, CoreError> {
532        if !self.is_built {
533            return Err(CoreError::InvalidServiceDescriptor {
534                message: "Container must be built before resolving services".to_string(),
535            });
536        }
537
538        let service_id = ServiceId::of::<T>();
539
540        // Check if we have a cached instance
541        {
542            let instances = self.instances.read().map_err(|_| CoreError::LockError {
543                resource: "service_instances".to_string(),
544            })?;
545
546            if let Some(ServiceInstance::Singleton(instance)) = instances.get(&service_id) {
547                return instance
548                    .clone()
549                    .downcast::<T>()
550                    .map_err(|_| CoreError::ServiceNotFound {
551                        service_type: std::any::type_name::<T>().to_string(),
552                    });
553            }
554        }
555
556        // Verify the service is configured for auto-wiring
557        let descriptor = self.bindings.get_descriptor(&service_id).ok_or_else(|| {
558            CoreError::ServiceNotFound {
559                service_type: std::any::type_name::<T>().to_string(),
560            }
561        })?;
562
563        let arc_instance = match &descriptor.activation_strategy {
564            crate::container::descriptor::ServiceActivationStrategy::AutoWired => {
565                // Create the service using Injectable
566                let service_instance = T::create(self)?;
567                Arc::new(service_instance)
568            }
569            crate::container::descriptor::ServiceActivationStrategy::Factory(_) => {
570                return Err(CoreError::InvalidServiceDescriptor {
571                    message: format!(
572                        "Service {} is configured with a factory but resolve_injectable was called. Use resolve() for factory-based services.",
573                        std::any::type_name::<T>()
574                    ),
575                });
576            }
577        };
578
579        // Cache if singleton
580        if descriptor.lifetime == ServiceScope::Singleton {
581            let mut instances = self.instances.write().map_err(|_| CoreError::LockError {
582                resource: "service_instances".to_string(),
583            })?;
584            instances.insert(service_id, ServiceInstance::Singleton(arc_instance.clone()));
585        }
586
587        Ok(arc_instance)
588    }
589
590    /// Resolve a trait object by downcasting from a concrete implementation
591    pub fn resolve_trait<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError> {
592        // For trait objects, we need special handling
593        // This is a placeholder - in a real implementation, we'd need metadata about
594        // which concrete type implements which trait
595        Err(CoreError::ServiceNotFound {
596            service_type: std::any::type_name::<T>().to_string(),
597        })
598    }
599
600    /// Bind a service token to a concrete implementation with transient lifetime
601    ///
602    /// This creates a mapping from a service token to a concrete implementation,
603    /// enabling semantic dependency resolution through tokens.
604    ///
605    /// ## Example
606    /// ```rust
607    /// use elif_core::container::{IocContainer, ServiceToken};
608    ///
609    /// // Define service trait and token
610    /// trait EmailService: Send + Sync {}
611    /// struct EmailToken;
612    /// impl ServiceToken for EmailToken {
613    ///     type Service = dyn EmailService;
614    /// }
615    ///
616    /// // Implementation
617    /// #[derive(Default)]
618    /// struct SmtpService;
619    /// impl EmailService for SmtpService {}
620    ///
621    /// // Bind token to implementation
622    /// let mut container = IocContainer::new();
623    /// container.bind_token::<EmailToken, SmtpService>()?;
624    /// # Ok::<(), Box<dyn std::error::Error>>(())
625    /// ```
626    pub fn bind_token<Token, Impl>(&mut self) -> Result<&mut Self, CoreError>
627    where
628        Token: ServiceToken,
629        Impl: Send + Sync + Default + 'static,
630    {
631        self.bind_token_with_lifetime::<Token, Impl>(ServiceScope::Transient)
632    }
633
634    /// Bind a service token to a concrete implementation as a singleton
635    pub fn bind_token_singleton<Token, Impl>(&mut self) -> Result<&mut Self, CoreError>
636    where
637        Token: ServiceToken,
638        Impl: Send + Sync + Default + 'static,
639    {
640        self.bind_token_with_lifetime::<Token, Impl>(ServiceScope::Singleton)
641    }
642
643    /// Bind a service token to a concrete implementation as a scoped service
644    pub fn bind_token_scoped<Token, Impl>(&mut self) -> Result<&mut Self, CoreError>
645    where
646        Token: ServiceToken,
647        Impl: Send + Sync + Default + 'static,
648    {
649        self.bind_token_with_lifetime::<Token, Impl>(ServiceScope::Scoped)
650    }
651
652    /// Bind a service token to a concrete implementation with a specific lifetime
653    pub fn bind_token_with_lifetime<Token, Impl>(
654        &mut self,
655        lifetime: ServiceScope,
656    ) -> Result<&mut Self, CoreError>
657    where
658        Token: ServiceToken,
659        Impl: Send + Sync + Default + 'static,
660    {
661        if self.is_built {
662            return Err(CoreError::InvalidServiceDescriptor {
663                message: "Cannot bind tokens after container is built".to_string(),
664            });
665        }
666
667        // Register the token binding
668        self.tokens
669            .register::<Token, Impl>()
670            .map_err(|e| CoreError::InvalidServiceDescriptor {
671                message: format!("Failed to register token binding: {}", e),
672            })?;
673
674        // Get the token binding to create a service descriptor
675        let token_binding = self.tokens.get_default::<Token>().ok_or_else(|| {
676            CoreError::InvalidServiceDescriptor {
677                message: "Failed to retrieve token binding after registration".to_string(),
678            }
679        })?;
680
681        // Create service descriptor for the implementation
682        let service_id = token_binding.to_service_id();
683
684        // Create a service descriptor directly with the token's service ID and specified lifetime
685        let descriptor = crate::container::descriptor::ServiceDescriptor {
686            service_id,
687            implementation_id: std::any::TypeId::of::<Impl>(),
688            lifetime,
689            activation_strategy: crate::container::descriptor::ServiceActivationStrategy::Factory(
690                Box::new(|| Ok(Box::new(Impl::default()) as Box<dyn Any + Send + Sync>)),
691            ),
692            dependencies: Vec::new(),
693        };
694
695        self.bindings.add_descriptor(descriptor);
696
697        Ok(self)
698    }
699
700    /// Bind a named service token to a concrete implementation
701    pub fn bind_token_named<Token, Impl>(
702        &mut self,
703        name: impl Into<String>,
704    ) -> Result<&mut Self, CoreError>
705    where
706        Token: ServiceToken,
707        Impl: Send + Sync + Default + 'static,
708    {
709        if self.is_built {
710            return Err(CoreError::InvalidServiceDescriptor {
711                message: "Cannot bind tokens after container is built".to_string(),
712            });
713        }
714
715        let name = name.into();
716
717        // Register the named token binding
718        self.tokens
719            .register_named::<Token, Impl>(&name)
720            .map_err(|e| CoreError::InvalidServiceDescriptor {
721                message: format!("Failed to register named token binding: {}", e),
722            })?;
723
724        // Get the token binding to create a service descriptor
725        let token_binding = self.tokens.get_named::<Token>(&name).ok_or_else(|| {
726            CoreError::InvalidServiceDescriptor {
727                message: "Failed to retrieve named token binding after registration".to_string(),
728            }
729        })?;
730
731        // Create service descriptor for the implementation
732        let service_id = token_binding.to_service_id();
733
734        // Create a service descriptor directly with the token's service ID
735        let descriptor = crate::container::descriptor::ServiceDescriptor {
736            service_id,
737            implementation_id: std::any::TypeId::of::<Impl>(),
738            lifetime: ServiceScope::Transient,
739            activation_strategy: crate::container::descriptor::ServiceActivationStrategy::Factory(
740                Box::new(|| Ok(Box::new(Impl::default()) as Box<dyn Any + Send + Sync>)),
741            ),
742            dependencies: Vec::new(),
743        };
744
745        self.bindings.add_descriptor(descriptor);
746
747        Ok(self)
748    }
749
750    /// Resolve a service by its token type
751    ///
752    /// This enables semantic dependency resolution where services are identified
753    /// by tokens rather than concrete types, enabling true dependency inversion.
754    ///
755    /// ## Example
756    /// ```rust
757    /// use std::sync::Arc;
758    /// use elif_core::container::{IocContainer, ServiceToken};
759    ///
760    /// // Define service trait and token
761    /// trait EmailService: Send + Sync {
762    ///     fn send(&self, to: &str, subject: &str, body: &str) -> Result<(), String>;
763    /// }
764    /// struct EmailToken;
765    /// impl ServiceToken for EmailToken {
766    ///     type Service = dyn EmailService;
767    /// }
768    ///
769    /// // Implementation
770    /// #[derive(Default)]
771    /// struct SmtpService;
772    /// impl EmailService for SmtpService {
773    ///     fn send(&self, _to: &str, _subject: &str, _body: &str) -> Result<(), String> {
774    ///         Ok(()) // Mock implementation
775    ///     }
776    /// }
777    ///
778    /// // Setup and resolve
779    /// let mut container = IocContainer::new();
780    /// container.bind_token::<EmailToken, SmtpService>()?;
781    /// container.build()?;
782    /// 
783    /// // Note: Trait object resolution is not yet fully implemented
784    /// // This will be available in a future version:
785    /// // let service: Arc<dyn EmailService> = container.resolve_by_token::<EmailToken>()?;
786    /// // service.send("user@example.com", "Welcome", "Hello world!")?;
787    /// # Ok::<(), Box<dyn std::error::Error>>(())
788    /// ```
789    pub fn resolve_by_token<Token>(&self) -> Result<Arc<Token::Service>, CoreError>
790    where
791        Token: ServiceToken,
792        Token::Service: 'static,
793    {
794        if !self.is_built {
795            return Err(CoreError::InvalidServiceDescriptor {
796                message: "Container must be built before resolving services".to_string(),
797            });
798        }
799
800        // Get the token binding
801        let token_binding =
802            self.tokens
803                .get_default::<Token>()
804                .ok_or_else(|| CoreError::ServiceNotFound {
805                    service_type: format!(
806                        "token {} -> {}",
807                        Token::token_type_name(),
808                        Token::service_type_name()
809                    ),
810                })?;
811
812        // Create service ID and resolve
813        let service_id = token_binding.to_service_id();
814
815        // Use a type-erased approach for trait object resolution
816        // We need to resolve the concrete implementation and cast it to the trait
817        self.resolve_by_id_as_trait::<Token::Service>(&service_id)
818    }
819
820    /// Resolve a named service by its token type
821    pub fn resolve_by_token_named<Token>(
822        &self,
823        name: &str,
824    ) -> Result<Arc<Token::Service>, CoreError>
825    where
826        Token: ServiceToken,
827        Token::Service: 'static,
828    {
829        if !self.is_built {
830            return Err(CoreError::InvalidServiceDescriptor {
831                message: "Container must be built before resolving services".to_string(),
832            });
833        }
834
835        // Get the named token binding
836        let token_binding =
837            self.tokens
838                .get_named::<Token>(name)
839                .ok_or_else(|| CoreError::ServiceNotFound {
840                    service_type: format!(
841                        "named token {}({}) -> {}",
842                        Token::token_type_name(),
843                        name,
844                        Token::service_type_name()
845                    ),
846                })?;
847
848        // Create service ID and resolve
849        let service_id = token_binding.to_service_id();
850
851        // Use a type-erased approach for trait object resolution
852        self.resolve_by_id_as_trait::<Token::Service>(&service_id)
853    }
854
855    /// Try to resolve a service by its token type, returning None if not found
856    pub fn try_resolve_by_token<Token>(&self) -> Option<Arc<Token::Service>>
857    where
858        Token: ServiceToken,
859        Token::Service: 'static,
860    {
861        self.resolve_by_token::<Token>().ok()
862    }
863
864    /// Try to resolve a named service by its token type, returning None if not found
865    pub fn try_resolve_by_token_named<Token>(&self, name: &str) -> Option<Arc<Token::Service>>
866    where
867        Token: ServiceToken,
868        Token::Service: 'static,
869    {
870        self.resolve_by_token_named::<Token>(name).ok()
871    }
872
873    /// Resolve a scoped service by its token type
874    ///
875    /// This resolves services within a specific scope, maintaining the lifecycle
876    /// and cleanup patterns expected by the existing scope management system.
877    pub fn resolve_by_token_scoped<Token>(
878        &self,
879        scope_id: &ScopeId,
880    ) -> Result<Arc<Token::Service>, CoreError>
881    where
882        Token: ServiceToken,
883        Token::Service: 'static,
884    {
885        if !self.is_built {
886            return Err(CoreError::InvalidServiceDescriptor {
887                message: "Container must be built before resolving services".to_string(),
888            });
889        }
890
891        // Get the token binding
892        let token_binding =
893            self.tokens
894                .get_default::<Token>()
895                .ok_or_else(|| CoreError::ServiceNotFound {
896                    service_type: format!(
897                        "token {} -> {}",
898                        Token::token_type_name(),
899                        Token::service_type_name()
900                    ),
901                })?;
902
903        // Create service ID and resolve in the specified scope
904        let service_id = token_binding.to_service_id();
905
906        // Use a type-erased approach for trait object resolution in scoped context
907        self.resolve_by_id_as_trait_scoped::<Token::Service>(&service_id, scope_id)
908    }
909
910    /// Resolve a named scoped service by its token type
911    pub fn resolve_by_token_named_scoped<Token>(
912        &self,
913        name: &str,
914        scope_id: &ScopeId,
915    ) -> Result<Arc<Token::Service>, CoreError>
916    where
917        Token: ServiceToken,
918        Token::Service: 'static,
919    {
920        if !self.is_built {
921            return Err(CoreError::InvalidServiceDescriptor {
922                message: "Container must be built before resolving services".to_string(),
923            });
924        }
925
926        // Get the named token binding
927        let token_binding =
928            self.tokens
929                .get_named::<Token>(name)
930                .ok_or_else(|| CoreError::ServiceNotFound {
931                    service_type: format!(
932                        "named token {}({}) -> {}",
933                        Token::token_type_name(),
934                        name,
935                        Token::service_type_name()
936                    ),
937                })?;
938
939        // Create service ID and resolve in the specified scope
940        let service_id = token_binding.to_service_id();
941
942        // Use a type-erased approach for trait object resolution in scoped context
943        self.resolve_by_id_as_trait_scoped::<Token::Service>(&service_id, scope_id)
944    }
945
946    /// Try to resolve a scoped service by its token type, returning None if not found
947    pub fn try_resolve_by_token_scoped<Token>(
948        &self,
949        scope_id: &ScopeId,
950    ) -> Option<Arc<Token::Service>>
951    where
952        Token: ServiceToken,
953        Token::Service: 'static,
954    {
955        self.resolve_by_token_scoped::<Token>(scope_id).ok()
956    }
957
958    /// Try to resolve a named scoped service by its token type, returning None if not found
959    pub fn try_resolve_by_token_named_scoped<Token>(
960        &self,
961        name: &str,
962        scope_id: &ScopeId,
963    ) -> Option<Arc<Token::Service>>
964    where
965        Token: ServiceToken,
966        Token::Service: 'static,
967    {
968        self.resolve_by_token_named_scoped::<Token>(name, scope_id)
969            .ok()
970    }
971
972    /// Check if a token is registered
973    pub fn contains_token<Token: ServiceToken>(&self) -> bool {
974        self.tokens.contains::<Token>()
975    }
976
977    /// Check if a named token is registered
978    pub fn contains_token_named<Token: ServiceToken>(&self, name: &str) -> bool {
979        self.tokens.contains_named::<Token>(name)
980    }
981
982    /// Get token registry statistics
983    pub fn token_stats(&self) -> crate::container::tokens::TokenRegistryStats {
984        self.tokens.stats()
985    }
986
987    /// Internal method to resolve services as trait objects
988    ///
989    /// This handles the complex type casting required for trait object resolution
990    fn resolve_by_id_as_trait<T: ?Sized + Send + Sync + 'static>(
991        &self,
992        service_id: &ServiceId,
993    ) -> Result<Arc<T>, CoreError> {
994        // For now, this is a simplified implementation
995        // In a full implementation, we would need more sophisticated trait object handling
996        // that involves storing metadata about how to cast concrete types to trait objects
997
998        // This is a placeholder that shows the intended API
999        // The actual implementation would require additional metadata in the token bindings
1000        Err(CoreError::ServiceNotFound {
1001            service_type: format!(
1002                "trait object resolution not yet fully implemented for service {}",
1003                service_id.type_name()
1004            ),
1005        })
1006    }
1007
1008    /// Internal method to resolve scoped services as trait objects
1009    ///
1010    /// This handles scoped trait object resolution with proper lifecycle management
1011    fn resolve_by_id_as_trait_scoped<T: ?Sized + Send + Sync + 'static>(
1012        &self,
1013        service_id: &ServiceId,
1014        _scope_id: &ScopeId,
1015    ) -> Result<Arc<T>, CoreError> {
1016        // For now, this is a simplified implementation
1017        // In a full implementation, this would integrate with the scoped service resolution
1018        // and maintain proper lifecycle management within the specified scope
1019
1020        // This is a placeholder that shows the intended API
1021        // The actual implementation would require additional metadata in the token bindings
1022        // and proper integration with the scope management system
1023        Err(CoreError::ServiceNotFound {
1024            service_type: format!(
1025                "scoped trait object resolution not yet fully implemented for service {}",
1026                service_id.type_name()
1027            ),
1028        })
1029    }
1030
1031    /// Check if a service is registered
1032    pub fn contains<T: 'static>(&self) -> bool {
1033        let service_id = ServiceId::of::<T>();
1034        self.bindings.contains(&service_id)
1035    }
1036
1037    /// Check if a named service is registered
1038    pub fn contains_named<T: 'static>(&self, name: &str) -> bool {
1039        self.bindings.contains_named::<T>(name)
1040    }
1041
1042    /// Get the number of registered services
1043    pub fn service_count(&self) -> usize {
1044        self.bindings.count()
1045    }
1046
1047    /// Get all registered service IDs
1048    pub fn registered_services(&self) -> Vec<ServiceId> {
1049        self.bindings.service_ids()
1050    }
1051
1052    /// Check if the container is built and ready
1053    pub fn is_built(&self) -> bool {
1054        self.is_built
1055    }
1056
1057    /// Validate the container configuration
1058    pub fn validate(&self) -> Result<(), CoreError> {
1059        if !self.is_built {
1060            return Err(CoreError::InvalidServiceDescriptor {
1061                message: "Container must be built before validation".to_string(),
1062            });
1063        }
1064
1065        // Validate dependency resolution
1066        if let Some(resolver) = &self.resolver {
1067            let service_ids = self.bindings.service_ids().into_iter().collect();
1068            resolver.validate_dependencies(&service_ids)?;
1069        }
1070
1071        Ok(())
1072    }
1073
1074    /// Resolve all implementations of an interface as a vector
1075    pub fn resolve_all<T: Send + Sync + 'static>(&self) -> Result<Vec<Arc<T>>, CoreError> {
1076        if !self.is_built {
1077            return Err(CoreError::InvalidServiceDescriptor {
1078                message: "Container must be built before resolving services".to_string(),
1079            });
1080        }
1081
1082        let mut implementations = Vec::new();
1083
1084        // Find all descriptors that match the type
1085        for descriptor in self.bindings.descriptors() {
1086            if descriptor.service_id.type_id == std::any::TypeId::of::<T>() {
1087                match self.resolve_by_id::<T>(&descriptor.service_id) {
1088                    Ok(instance) => implementations.push(instance),
1089                    Err(_) => continue, // Skip failed resolutions
1090                }
1091            }
1092        }
1093
1094        if implementations.is_empty() {
1095            return Err(CoreError::ServiceNotFound {
1096                service_type: std::any::type_name::<T>().to_string(),
1097            });
1098        }
1099
1100        Ok(implementations)
1101    }
1102
1103    /// Resolve all implementations of an interface as a HashMap with their names
1104    pub fn resolve_all_named<T: Send + Sync + 'static>(
1105        &self,
1106    ) -> Result<std::collections::HashMap<String, Arc<T>>, CoreError> {
1107        if !self.is_built {
1108            return Err(CoreError::InvalidServiceDescriptor {
1109                message: "Container must be built before resolving services".to_string(),
1110            });
1111        }
1112
1113        let mut implementations = std::collections::HashMap::new();
1114
1115        // Find all named descriptors that match the type
1116        for descriptor in self.bindings.descriptors() {
1117            if descriptor.service_id.type_id == std::any::TypeId::of::<T>() {
1118                if let Some(name) = &descriptor.service_id.name {
1119                    match self.resolve_by_id::<T>(&descriptor.service_id) {
1120                        Ok(instance) => {
1121                            implementations.insert(name.clone(), instance);
1122                        }
1123                        Err(_) => continue, // Skip failed resolutions
1124                    }
1125                }
1126            }
1127        }
1128
1129        if implementations.is_empty() {
1130            return Err(CoreError::ServiceNotFound {
1131                service_type: format!("named implementations of {}", std::any::type_name::<T>()),
1132            });
1133        }
1134
1135        Ok(implementations)
1136    }
1137
1138    /// Get default implementation for a type (marked with is_default in BindingConfig)
1139    pub fn resolve_default<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError> {
1140        // For now, just resolve the first unnamed implementation
1141        // In a full implementation, we'd track which binding was marked as default
1142        self.resolve::<T>()
1143    }
1144
1145    /// Get service information for debugging
1146    pub fn get_service_info<T: 'static>(&self) -> Option<String> {
1147        let service_id = ServiceId::of::<T>();
1148        self.bindings
1149            .get_descriptor(&service_id)
1150            .map(|desc| format!("{:?}", desc))
1151    }
1152
1153    /// Get all registered service IDs for debugging
1154    pub fn get_registered_services(&self) -> Vec<String> {
1155        self.bindings
1156            .service_ids()
1157            .into_iter()
1158            .map(|id| {
1159                format!(
1160                    "{} ({})",
1161                    id.type_name(),
1162                    id.name.unwrap_or_else(|| "default".to_string())
1163                )
1164            })
1165            .collect()
1166    }
1167
1168    /// Validate that all registered services can be resolved
1169    pub fn validate_all_services(&self) -> Result<(), Vec<CoreError>> {
1170        if !self.is_built {
1171            return Err(vec![CoreError::InvalidServiceDescriptor {
1172                message: "Container must be built before validation".to_string(),
1173            }]);
1174        }
1175
1176        let mut errors = Vec::new();
1177
1178        for descriptor in self.bindings.descriptors() {
1179            // Validate dependencies exist
1180            for dependency in &descriptor.dependencies {
1181                if !self.bindings.contains(dependency) {
1182                    errors.push(CoreError::ServiceNotFound {
1183                        service_type: format!(
1184                            "{} (dependency of {})",
1185                            dependency.type_name(),
1186                            descriptor.service_id.type_name()
1187                        ),
1188                    });
1189                }
1190            }
1191        }
1192
1193        if errors.is_empty() {
1194            Ok(())
1195        } else {
1196            Err(errors)
1197        }
1198    }
1199
1200    /// Get service statistics
1201    pub fn get_statistics(&self) -> ServiceStatistics {
1202        let mut stats = ServiceStatistics::default();
1203
1204        stats.total_services = self.bindings.count();
1205        stats.singleton_services = 0;
1206        stats.transient_services = 0;
1207        stats.scoped_services = 0;
1208        stats.cached_instances = 0;
1209
1210        for descriptor in self.bindings.descriptors() {
1211            match descriptor.lifetime {
1212                crate::container::scope::ServiceScope::Singleton => stats.singleton_services += 1,
1213                crate::container::scope::ServiceScope::Transient => stats.transient_services += 1,
1214                crate::container::scope::ServiceScope::Scoped => stats.scoped_services += 1,
1215            }
1216        }
1217
1218        if let Ok(instances) = self.instances.read() {
1219            stats.cached_instances = instances.len();
1220        }
1221
1222        stats
1223    }
1224}
1225
1226/// Service statistics for monitoring and debugging
1227#[derive(Debug, Default)]
1228pub struct ServiceStatistics {
1229    pub total_services: usize,
1230    pub singleton_services: usize,
1231    pub transient_services: usize,
1232    pub scoped_services: usize,
1233    pub cached_instances: usize,
1234}
1235
1236impl ServiceBinder for IocContainer {
1237    fn add_service_descriptor(
1238        &mut self,
1239        descriptor: crate::container::descriptor::ServiceDescriptor,
1240    ) -> Result<&mut Self, CoreError> {
1241        if self.is_built {
1242            return Err(CoreError::InvalidServiceDescriptor {
1243                message: "Cannot add service descriptors after container is built".to_string(),
1244            });
1245        }
1246        self.bindings.add_descriptor(descriptor);
1247        Ok(self)
1248    }
1249
1250    fn bind<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1251        &mut self,
1252    ) -> &mut Self {
1253        if self.is_built {
1254            panic!("Cannot add bindings after container is built");
1255        }
1256        self.bindings.bind::<TInterface, TImpl>();
1257        self
1258    }
1259
1260    fn bind_singleton<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1261        &mut self,
1262    ) -> &mut Self {
1263        if self.is_built {
1264            panic!("Cannot add bindings after container is built");
1265        }
1266        self.bindings.bind_singleton::<TInterface, TImpl>();
1267        self
1268    }
1269
1270    fn bind_transient<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1271        &mut self,
1272    ) -> &mut Self {
1273        if self.is_built {
1274            panic!("Cannot add bindings after container is built");
1275        }
1276        self.bindings.bind_transient::<TInterface, TImpl>();
1277        self
1278    }
1279
1280    fn bind_factory<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
1281    where
1282        F: Fn() -> Result<T, CoreError> + Send + Sync + 'static,
1283        T: Send + Sync + 'static,
1284    {
1285        if self.is_built {
1286            panic!("Cannot add bindings after container is built");
1287        }
1288        self.bindings.bind_factory::<TInterface, _, _>(factory);
1289        self
1290    }
1291
1292    fn bind_instance<TInterface: ?Sized + 'static, TImpl: Send + Sync + Clone + 'static>(
1293        &mut self,
1294        instance: TImpl,
1295    ) -> &mut Self {
1296        if self.is_built {
1297            panic!("Cannot add bindings after container is built");
1298        }
1299        self.bindings.bind_instance::<TInterface, TImpl>(instance);
1300        self
1301    }
1302
1303    fn bind_named<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1304        &mut self,
1305        name: &str,
1306    ) -> &mut Self {
1307        if self.is_built {
1308            panic!("Cannot add bindings after container is built");
1309        }
1310        self.bindings.bind_named::<TInterface, TImpl>(name);
1311        self
1312    }
1313
1314    fn bind_injectable<T: Injectable>(&mut self) -> &mut Self {
1315        if self.is_built {
1316            panic!("Cannot add bindings after container is built");
1317        }
1318        self.bindings.bind_injectable::<T>();
1319        self
1320    }
1321
1322    fn bind_injectable_singleton<T: Injectable>(&mut self) -> &mut Self {
1323        if self.is_built {
1324            panic!("Cannot add bindings after container is built");
1325        }
1326        self.bindings.bind_injectable_singleton::<T>();
1327        self
1328    }
1329
1330    // Advanced binding methods implementation
1331
1332    fn bind_with<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1333        &mut self,
1334    ) -> crate::container::binding::AdvancedBindingBuilder<TInterface> {
1335        if self.is_built {
1336            panic!("Cannot add bindings after container is built");
1337        }
1338        self.bindings.bind_with::<TInterface, TImpl>()
1339    }
1340
1341    fn with_implementation<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
1342        &mut self,
1343        config: crate::container::binding::BindingConfig,
1344    ) -> &mut Self {
1345        if self.is_built {
1346            panic!("Cannot add bindings after container is built");
1347        }
1348        self.bindings
1349            .with_implementation::<TInterface, TImpl>(config);
1350        self
1351    }
1352
1353    fn bind_lazy<TInterface: ?Sized + 'static, F, T>(&mut self, factory: F) -> &mut Self
1354    where
1355        F: Fn() -> T + Send + Sync + 'static,
1356        T: Send + Sync + 'static,
1357    {
1358        if self.is_built {
1359            panic!("Cannot add bindings after container is built");
1360        }
1361        self.bindings.bind_lazy::<TInterface, F, T>(factory);
1362        self
1363    }
1364
1365    fn bind_parameterized_factory<TInterface: ?Sized + 'static, P, F, T>(
1366        &mut self,
1367        factory: F,
1368    ) -> &mut Self
1369    where
1370        F: Fn(P) -> Result<T, CoreError> + Send + Sync + 'static,
1371        T: Send + Sync + 'static,
1372        P: Send + Sync + 'static,
1373    {
1374        if self.is_built {
1375            panic!("Cannot add bindings after container is built");
1376        }
1377        self.bindings
1378            .bind_parameterized_factory::<TInterface, P, F, T>(factory);
1379        self
1380    }
1381
1382    fn bind_collection<TInterface: ?Sized + 'static, F>(&mut self, configure: F) -> &mut Self
1383    where
1384        F: FnOnce(&mut crate::container::binding::CollectionBindingBuilder<TInterface>),
1385    {
1386        if self.is_built {
1387            panic!("Cannot add bindings after container is built");
1388        }
1389        self.bindings.bind_collection::<TInterface, F>(configure);
1390        self
1391    }
1392}
1393
1394impl Default for IocContainer {
1395    fn default() -> Self {
1396        Self::new()
1397    }
1398}
1399
1400impl DependencyResolver for IocContainer {
1401    fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError> {
1402        self.resolve::<T>()
1403    }
1404
1405    fn resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Result<Arc<T>, CoreError> {
1406        self.resolve_named::<T>(name)
1407    }
1408
1409    fn try_resolve<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
1410        self.try_resolve::<T>()
1411    }
1412
1413    fn try_resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>> {
1414        self.try_resolve_named::<T>(name)
1415    }
1416}
1417
1418#[cfg(test)]
1419mod tests {
1420    use super::*;
1421
1422    trait TestRepository: Send + Sync {
1423        fn find(&self, id: u32) -> Option<String>;
1424    }
1425
1426    #[derive(Default)]
1427    struct PostgresRepository;
1428
1429    unsafe impl Send for PostgresRepository {}
1430    unsafe impl Sync for PostgresRepository {}
1431
1432    impl TestRepository for PostgresRepository {
1433        fn find(&self, _id: u32) -> Option<String> {
1434            Some("postgres_data".to_string())
1435        }
1436    }
1437
1438    trait TestService: Send + Sync {
1439        fn get_data(&self) -> String;
1440    }
1441
1442    #[derive(Default)]
1443    struct UserService;
1444
1445    unsafe impl Send for UserService {}
1446    unsafe impl Sync for UserService {}
1447
1448    impl TestService for UserService {
1449        fn get_data(&self) -> String {
1450            "user_data".to_string()
1451        }
1452    }
1453
1454    #[test]
1455    fn test_basic_binding_and_resolution() {
1456        let mut container = IocContainer::new();
1457
1458        container
1459            .bind::<PostgresRepository, PostgresRepository>()
1460            .bind_singleton::<UserService, UserService>();
1461
1462        container.build().unwrap();
1463
1464        let repo = container.resolve::<PostgresRepository>().unwrap();
1465        assert_eq!(repo.find(1), Some("postgres_data".to_string()));
1466
1467        let service = container.resolve::<UserService>().unwrap();
1468        assert_eq!(service.get_data(), "user_data");
1469    }
1470
1471    #[test]
1472    fn test_named_services() {
1473        let mut container = IocContainer::new();
1474
1475        container
1476            .bind_named::<PostgresRepository, PostgresRepository>("postgres")
1477            .bind_named::<PostgresRepository, PostgresRepository>("backup");
1478
1479        container.build().unwrap();
1480
1481        let postgres_repo = container
1482            .resolve_named::<PostgresRepository>("postgres")
1483            .unwrap();
1484        let backup_repo = container
1485            .resolve_named::<PostgresRepository>("backup")
1486            .unwrap();
1487
1488        assert_eq!(postgres_repo.find(1), Some("postgres_data".to_string()));
1489        assert_eq!(backup_repo.find(1), Some("postgres_data".to_string()));
1490    }
1491
1492    #[test]
1493    fn test_singleton_behavior() {
1494        let mut container = IocContainer::new();
1495
1496        container.bind_singleton::<UserService, UserService>();
1497        container.build().unwrap();
1498
1499        let service1 = container.resolve::<UserService>().unwrap();
1500        let service2 = container.resolve::<UserService>().unwrap();
1501
1502        // Should be the same instance
1503        assert!(Arc::ptr_eq(&service1, &service2));
1504    }
1505
1506    #[test]
1507    fn test_transient_behavior() {
1508        let mut container = IocContainer::new();
1509
1510        container.bind_transient::<UserService, UserService>();
1511        container.build().unwrap();
1512
1513        let service1 = container.resolve::<UserService>().unwrap();
1514        let service2 = container.resolve::<UserService>().unwrap();
1515
1516        // Should be different instances
1517        assert!(!Arc::ptr_eq(&service1, &service2));
1518    }
1519
1520    #[test]
1521    #[should_panic(expected = "Cannot add bindings after container is built")]
1522    fn test_cannot_bind_after_build() {
1523        let mut container = IocContainer::new();
1524        container.build().unwrap();
1525
1526        // This should panic
1527        container.bind::<UserService, UserService>();
1528    }
1529
1530    #[test]
1531    fn test_service_not_found() {
1532        let mut container = IocContainer::new();
1533        container.build().unwrap();
1534
1535        let result = container.resolve::<UserService>();
1536        assert!(result.is_err());
1537
1538        if let Err(CoreError::ServiceNotFound { service_type }) = result {
1539            assert!(service_type.contains("UserService"));
1540        }
1541    }
1542}