ferrous_di/provider/
mod.rs

1//! Service provider module for dependency injection.
2//!
3//! This module contains the ServiceProvider type and related functionality
4//! for resolving registered services from the DI container.
5
6use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9use crate::{DiResult, DiError, Key, Lifetime};
10use crate::registration::{Registry, AnyArc};
11use crate::internal::{DisposeBag, BoxFutureUnit, with_circular_catch};
12use crate::observer::{Observers, ObservationContext};
13use crate::capabilities::{CapabilityRegistry, ToolSelectionCriteria, ToolDiscoveryResult, ToolInfo};
14use crate::fast_singletons::FastSingletonCache;
15use crate::traits::{Resolver, ResolverCore, Dispose, AsyncDispose};
16
17// Re-export Scope and ResolverContext
18pub mod scope;
19pub mod context;
20pub use scope::*;
21pub use context::ResolverContext;
22use context::ResolverContext as LocalResolverContext;
23
24/// Service provider for resolving dependencies from the DI container.
25///
26/// The `ServiceProvider` is the heart of the dependency injection system. It resolves
27/// services according to their registered lifetimes (Singleton, Scoped, Transient) and
28/// manages the lifecycle of singleton services including disposal.
29///
30/// # Performance Optimizations
31///
32/// ServiceProvider includes world-class performance optimizations:
33/// - **Singleton caching**: Embedded OnceCell provides 31ns resolution (~31.5M ops/sec)
34/// - **Scoped caching**: Slot-based resolution with O(1) access times  
35/// - **Hybrid registry**: Vec for small collections, HashMap for large ones
36/// - **Lock-free reads**: After initialization, singleton access requires no locks
37///
38/// # Thread Safety
39/// 
40/// ServiceProvider is fully thread-safe and can be shared across multiple threads.
41/// Singleton services are cached with proper synchronization, and the provider
42/// can be cloned cheaply (it uses `Arc` internally).
43///
44/// # Examples
45///
46/// ```
47/// use ferrous_di::{ServiceCollection, Resolver};
48/// use std::sync::Arc;
49///
50/// struct Database { url: String }
51/// struct UserService { db: Arc<Database> }
52///
53/// let mut collection = ServiceCollection::new();
54/// collection.add_singleton(Database { url: "postgres://localhost".to_string() });
55/// collection.add_transient_factory::<UserService, _>(|resolver| {
56///     UserService { db: resolver.get_required::<Database>() }
57/// });
58///
59/// let provider = collection.build();
60/// let user_service = provider.get_required::<UserService>();
61/// assert_eq!(user_service.db.url, "postgres://localhost");
62/// ```
63pub struct ServiceProvider {
64    inner: Arc<ProviderInner>,
65}
66
67pub(crate) struct ProviderInner {
68    pub registry: Registry,
69    pub singletons: Mutex<HashMap<Key, AnyArc>>, // Legacy cache for multi-bindings
70    pub fast_cache: FastSingletonCache, // High-performance singleton cache
71    pub root_disposers: Mutex<DisposeBag>,
72    pub observers: Observers,
73    pub capabilities: CapabilityRegistry,
74}
75
76impl ServiceProvider {
77    /// Convenience accessor for the inner provider
78    #[inline]
79    pub(crate) fn inner(&self) -> &ProviderInner {
80        &self.inner
81    }
82
83    /// Creates a new scope for resolving scoped services.
84    ///
85    /// Scoped services are cached per scope and are ideal for request-scoped
86    /// dependencies in web applications. Each scope maintains its own cache
87    /// of scoped services while still accessing singleton services from the
88    /// root provider.
89    ///
90    /// # Returns
91    ///
92    /// A new `Scope` that can resolve both scoped and singleton services.
93    /// The scope maintains its own cache for scoped services.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use ferrous_di::{ServiceCollection, Resolver};
99    /// use std::sync::{Arc, Mutex};
100    ///
101    /// #[derive(Debug)]
102    /// struct RequestId(String);
103    ///
104    /// let mut collection = ServiceCollection::new();
105    /// let counter = Arc::new(Mutex::new(0));
106    /// let counter_clone = counter.clone();
107    ///
108    /// collection.add_scoped_factory::<RequestId, _>(move |_| {
109    ///     let mut c = counter_clone.lock().unwrap();
110    ///     *c += 1;
111    ///     RequestId(format!("req-{}", *c))
112    /// });
113    ///
114    /// let provider = collection.build();
115    ///
116    /// // Create separate scopes
117    /// let scope1 = provider.create_scope();
118    /// let scope2 = provider.create_scope();
119    ///
120    /// let req1a = scope1.get_required::<RequestId>();
121    /// let req1b = scope1.get_required::<RequestId>(); // Same instance
122    /// let req2 = scope2.get_required::<RequestId>(); // Different instance
123    ///
124    /// assert!(Arc::ptr_eq(&req1a, &req1b)); // Same scope, same instance
125    /// assert!(!Arc::ptr_eq(&req1a, &req2)); // Different scopes, different instances
126    /// ```
127    pub fn create_scope(&self) -> Scope {
128        #[cfg(feature = "once-cell")]
129        {
130            use once_cell::sync::OnceCell;
131            
132            let scoped_count = self.inner().registry.scoped_count;
133            let scoped_cells: Box<[OnceCell<AnyArc>]> = (0..scoped_count)
134                .map(|_| OnceCell::new())
135                .collect::<Vec<_>>()
136                .into_boxed_slice();
137                
138            Scope {
139                root: self.clone(),
140                scoped_cells,
141                scoped_disposers: Mutex::new(DisposeBag::default()),
142            }
143        }
144        
145        #[cfg(not(feature = "once-cell"))]
146        {
147            Scope {
148                root: self.clone(),
149                scoped: Mutex::new(HashMap::new()),
150                scoped_disposers: Mutex::new(DisposeBag::default()),
151            }
152        }
153    }
154
155    /// Disposes all registered disposal hooks in LIFO order.
156    ///
157    /// This method runs all asynchronous disposal hooks first (in reverse order),
158    /// followed by all synchronous disposal hooks (in reverse order). This ensures
159    /// proper cleanup of singleton services.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use ferrous_di::{ServiceCollection, Dispose, AsyncDispose, Resolver};
165    /// use async_trait::async_trait;
166    /// use std::sync::Arc;
167    ///
168    /// struct Cache;
169    /// impl Dispose for Cache {
170    ///     fn dispose(&self) {
171    ///         println!("Cache disposed");
172    ///     }
173    /// }
174    ///
175    /// struct Client;
176    /// #[async_trait]
177    /// impl AsyncDispose for Client {
178    ///     async fn dispose(&self) {
179    ///         println!("Client disposed");
180    ///     }
181    /// }
182    ///
183    /// # async fn example() {
184    /// let mut services = ServiceCollection::new();
185    /// services.add_singleton_factory::<Cache, _>(|r| {
186    ///     let cache = Arc::new(Cache);
187    ///     r.register_disposer(cache.clone());
188    ///     Cache // Return concrete type
189    /// });
190    /// services.add_singleton_factory::<Client, _>(|r| {
191    ///     let client = Arc::new(Client);
192    ///     r.register_async_disposer(client.clone());
193    ///     Client // Return concrete type
194    /// });
195    ///
196    /// let provider = services.build();
197    /// // ... use services ...
198    /// provider.dispose_all().await;
199    /// # }
200    /// ```
201    pub async fn dispose_all(&self) {
202        // First run async disposers in reverse order
203        self.inner().root_disposers.lock().unwrap().run_all_async_reverse().await;
204        // Then run sync disposers in reverse order  
205        self.inner().root_disposers.lock().unwrap().run_all_sync_reverse();
206    }
207    
208    #[cfg(feature = "diagnostics")]
209    pub fn to_debug_string(&self) -> String {
210        let mut s = String::new();
211        s.push_str("=== Service Provider Debug ===\n");
212        s.push_str("Single Bindings:\n");
213        for (k, r) in self.inner().registry.iter() {
214            s.push_str(&format!("  {:?}: {:?}\n", k, r.lifetime));
215        }
216        s.push_str("Multi Bindings:\n");
217        for (k, rs) in &self.inner().registry.many {
218            for (i, r) in rs.iter().enumerate() {
219                s.push_str(&format!("  MultiTrait({} @ {}): {:?}\n", k, i, r.lifetime));
220            }
221        }
222        s
223    }
224}
225
226impl Clone for ServiceProvider {
227    fn clone(&self) -> Self {
228        Self {
229            inner: self.inner.clone(),
230        }
231    }
232}
233
234impl Drop for ServiceProvider {
235    fn drop(&mut self) {
236        // Check if this is the last reference to the inner provider
237        if Arc::strong_count(&self.inner) == 1 {
238            // Check if there are undisposed resources and warn
239            if let Ok(bag) = self.inner.root_disposers.try_lock() {
240                if !bag.is_empty() {
241                    eprintln!("[ferrous-di] ServiceProvider dropped with undisposed resources. Call dispose_all().await before dropping.");
242                }
243            }
244        }
245    }
246}
247
248impl ResolverCore for ServiceProvider {
249    fn resolve_any(&self, key: &Key) -> DiResult<AnyArc> {
250        let name = key.display_name();
251        with_circular_catch(name, || self.resolve_any_impl(key))
252    }
253    
254    fn resolve_many(&self, key: &Key) -> DiResult<Vec<AnyArc>> {
255        if let Key::Trait(_trait_name) = key {
256            let name = key.display_name();
257            with_circular_catch(name, || self.resolve_many_impl(key))
258        } else {
259            Ok(Vec::new())
260        }
261    }
262
263    fn push_sync_disposer(&self, f: Box<dyn FnOnce() + Send>) {
264        self.inner().root_disposers.lock().unwrap().push_sync(f);
265    }
266
267    fn push_async_disposer(&self, f: Box<dyn FnOnce() -> BoxFutureUnit + Send>) {
268        self.inner().root_disposers.lock().unwrap().push_async(move || (f)());
269    }
270}
271
272impl ServiceProvider {
273    /// Alternative high-performance singleton resolution using FastSingletonCache
274    /// This provides an alternative to the embedded OnceCell approach for scenarios
275    /// where maximum throughput is needed and error handling can be simplified
276    #[inline(always)]
277    pub fn resolve_singleton_fast_cache(&self, key: &Key) -> Option<AnyArc> {
278        // Check if we already have it in the fast cache
279        if let Some(cached) = self.inner().fast_cache.get(key) {
280            return Some(cached);
281        }
282        
283        // If not cached and it's a singleton, try to initialize it
284        if let Some(reg) = self.inner().registry.get(key) {
285            if reg.lifetime == Lifetime::Singleton {
286                // Use the fast cache for ultra-high performance
287                // Note: This version doesn't propagate errors for maximum performance
288                let result = self.inner().fast_cache.get_or_init(key, || {
289                    let ctx = LocalResolverContext::new(self);
290                    (reg.ctor)(&ctx).unwrap_or_else(|_| Arc::new(()) as AnyArc)
291                });
292                return Some(result);
293            }
294        }
295        None
296    }
297
298    /// Ultra-optimized singleton resolution using embedded OnceCell
299    #[inline(always)]
300    pub(crate) fn resolve_singleton(&self, reg: &crate::registration::Registration, _key: &Key) -> DiResult<AnyArc> {
301        #[cfg(feature = "once-cell")]
302        {
303            if let Some(cell) = &reg.single_runtime {
304                // Ultra-fast path: check if already initialized
305                if let Some(value) = cell.get() {
306                    return Ok(value.clone());
307                }
308                
309                // Slow path: initialize with factory (unlikely after first access)
310                // TODO: Add std::hint::unlikely when stable
311                {
312                    let ctx = LocalResolverContext::new(self);
313                    let v = (reg.ctor)(&ctx)?;
314                    let stored = cell.get_or_init(|| v.clone()).clone();
315                    return Ok(stored);
316                }
317            }
318        }
319        
320        #[cfg(not(feature = "once-cell"))]
321        {
322            if let Some(mutex) = &reg.single_runtime {
323                let mut guard = mutex.lock().unwrap();
324                if let Some(value) = guard.as_ref() {
325                    return Ok(value.clone());
326                }
327                
328                let ctx = LocalResolverContext::new(self);
329                let value = (reg.ctor)(&ctx)?;
330                *guard = Some(value.clone());
331                return Ok(value);
332            }
333        }
334        
335        // Fallback to old behavior if no single_runtime (shouldn't happen)
336        let ctx = LocalResolverContext::new(self);
337        (reg.ctor)(&ctx)
338    }
339    
340    /// Creates observation context from available scope-local data.
341    fn create_observation_context(&self) -> ObservationContext {
342        // Try to extract workflow context from scope-local storage
343        // This allows rich observation when workflow context is available
344        use crate::scope_local::{ScopeLocal, WorkflowContext};
345        
346        // Attempt to get workflow context if available
347        if let Ok(workflow_ctx) = self.get::<ScopeLocal<WorkflowContext>>() {
348            ObservationContext::workflow(
349                workflow_ctx.run_id(),
350                workflow_ctx.workflow_name(),
351                None::<String>
352            )
353        } else {
354            // Fallback to basic context
355            ObservationContext::new()
356        }
357    }
358    
359    fn resolve_any_impl(&self, key: &Key) -> DiResult<AnyArc> {
360        let name = key.display_name();
361        
362        if let Some(reg) = self.inner().registry.get(key) {
363            match reg.lifetime {
364                Lifetime::Singleton => {
365                    // Observer support with optimized path
366                    if self.inner().observers.has_observers() {
367                        let start = std::time::Instant::now();
368                        let context = self.create_observation_context();
369                        self.inner().observers.resolving_with_context(key, &context);
370                        
371                        let result = self.resolve_singleton(reg, key);
372                        
373                        let duration = start.elapsed();
374                        self.inner().observers.resolved_with_context(key, duration, &context);
375                        result
376                    } else {
377                        // Ultra-fast path: no observer overhead
378                        self.resolve_singleton(reg, key)
379                    }
380                }
381                Lifetime::Scoped => {
382                    Err(DiError::WrongLifetime("Cannot resolve scoped service from root provider"))
383                }
384                Lifetime::Transient => {
385                    if self.inner().observers.has_observers() {
386                        let start = std::time::Instant::now();
387                        let context = self.create_observation_context();
388                        self.inner().observers.resolving_with_context(key, &context);
389                        
390                        let ctx = LocalResolverContext::new(self);
391                        let result = (reg.ctor)(&ctx);
392                        
393                        match &result {
394                            Ok(_) => {
395                                let duration = start.elapsed();
396                                self.inner().observers.resolved_with_context(key, duration, &context);
397                            }
398                            Err(_) => {
399                                let duration = start.elapsed();
400                                self.inner().observers.resolved_with_context(key, duration, &context);
401                            }
402                        }
403                        result
404                    } else {
405                        let ctx = LocalResolverContext::new(self);
406                        (reg.ctor)(&ctx)
407                    }
408                }
409            }
410        } else if let Key::Trait(trait_name) = key {
411            // Fallback: if trait has multi-bindings, return last as single
412            if let Some(regs) = self.inner().registry.many.get(trait_name) {
413                if let Some(last) = regs.last() {
414                    if self.inner().observers.has_observers() {
415                        let start = std::time::Instant::now();
416                        let context = self.create_observation_context();
417                        self.inner().observers.resolving_with_context(key, &context);
418                        
419                        let ctx = LocalResolverContext::new(self);
420                        let result = (last.ctor)(&ctx);
421                        
422                        match &result {
423                            Ok(_) => {
424                                let duration = start.elapsed();
425                                self.inner().observers.resolved_with_context(key, duration, &context);
426                            }
427                            Err(_) => {
428                                let duration = start.elapsed();
429                                self.inner().observers.resolved_with_context(key, duration, &context);
430                            }
431                        }
432                        result
433                    } else {
434                        let ctx = LocalResolverContext::new(self);
435                        (last.ctor)(&ctx)
436                    }
437                } else {
438                    Err(DiError::NotFound(name))
439                }
440            } else {
441                Err(DiError::NotFound(name))
442            }
443        } else {
444            Err(DiError::NotFound(name))
445        }
446    }
447    
448    fn resolve_many_impl(&self, key: &Key) -> DiResult<Vec<AnyArc>> {
449        if let Key::Trait(trait_name) = key {
450            if let Some(regs) = self.inner().registry.many.get(trait_name) {
451                let mut results = Vec::with_capacity(regs.len());
452                
453                for (i, reg) in regs.iter().enumerate() {
454                    let multi_key = Key::MultiTrait(trait_name, i);
455                    
456                    let value = match reg.lifetime {
457                        Lifetime::Singleton => {
458                            // Expert fix: Double-checked locking - never hold lock while invoking factory
459                            {
460                                let cache = self.inner().singletons.lock().unwrap();
461                                if let Some(cached) = cache.get(&multi_key) {
462                                    results.push(cached.clone());
463                                    continue;
464                                }
465                            } // Lock released here
466                            
467                            // Create without holding lock
468                            let ctx = ResolverContext::new(self);
469                            let value = (reg.ctor)(&ctx)?;
470                            
471                            // Double-checked insert
472                            {
473                                let mut cache = self.inner().singletons.lock().unwrap();
474                                if let Some(cached) = cache.get(&multi_key) {
475                                    cached.clone() // Another thread beat us
476                                } else {
477                                    cache.insert(multi_key, value.clone());
478                                    value
479                                }
480                            }
481                        }
482                        Lifetime::Scoped => {
483                            return Err(DiError::WrongLifetime("Cannot resolve scoped service from root provider"));
484                        }
485                        Lifetime::Transient => {
486                            let ctx = ResolverContext::new(self);
487                            (reg.ctor)(&ctx)?
488                        }
489                    };
490                    
491                    results.push(value);
492                }
493                
494                Ok(results)
495            } else {
496                Ok(Vec::new())
497            }
498        } else {
499            Ok(Vec::new())
500        }
501    }
502
503    /// Create a new ServiceProvider with the given registry.
504    /// This is used internally by ServiceCollection.build().
505    #[allow(dead_code)]
506    pub(crate) fn new(registry: Registry) -> Self {
507        Self::new_with_observers_and_capabilities(registry, Observers::new(), CapabilityRegistry::new())
508    }
509
510    /// Create a new ServiceProvider with the given registry and observers.
511    /// This is used internally by ServiceCollection.build().
512    #[allow(dead_code)]
513    pub(crate) fn new_with_observers(registry: Registry, observers: Observers) -> Self {
514        Self::new_with_observers_and_capabilities(registry, observers, CapabilityRegistry::new())
515    }
516
517    /// Create a new ServiceProvider with the given registry, observers, and capabilities.
518    /// This is used internally by ServiceCollection.build().
519    pub(crate) fn new_with_observers_and_capabilities(
520        registry: Registry, 
521        observers: Observers, 
522        capabilities: CapabilityRegistry
523    ) -> Self {
524        Self {
525            inner: Arc::new(ProviderInner {
526                registry,
527                singletons: Mutex::new(HashMap::new()), // Legacy cache for multi-bindings
528                fast_cache: FastSingletonCache::new(), // High-performance singleton cache
529                root_disposers: Mutex::new(DisposeBag::default()),
530                observers,
531                capabilities,
532            }),
533        }
534    }
535
536    /// Discovers available tools based on capability requirements.
537    ///
538    /// This is the main entry point for agent planners to find suitable tools
539    /// for their tasks. Returns matching tools along with partial matches and
540    /// any unsatisfied requirements.
541    ///
542    /// # Examples
543    ///
544    /// ```
545    /// use ferrous_di::{ServiceCollection, ToolSelectionCriteria, CapabilityRequirement};
546    ///
547    /// // ... after registering tools with capabilities ...
548    /// let mut services = ServiceCollection::new();
549    /// let provider = services.build();
550    ///
551    /// // Find tools that can search the web
552    /// let criteria = ToolSelectionCriteria::new()
553    ///     .require("web_search")
554    ///     .exclude_tag("experimental")
555    ///     .max_cost(0.01);
556    ///
557    /// let result = provider.discover_tools(&criteria);
558    ///
559    /// println!("Found {} matching tools", result.matching_tools.len());
560    /// for tool in &result.matching_tools {
561    ///     println!("  - {}: {}", tool.name, tool.description);
562    /// }
563    ///
564    /// if !result.unsatisfied_requirements.is_empty() {
565    ///     println!("Missing capabilities: {:?}", result.unsatisfied_requirements);
566    /// }
567    /// ```
568    pub fn discover_tools(&self, criteria: &ToolSelectionCriteria) -> ToolDiscoveryResult {
569        self.inner.capabilities.discover(criteria)
570    }
571    
572    /// Gets all registered tools with their capability information.
573    ///
574    /// Useful for debugging or building tool catalogs.
575    pub fn list_all_tools(&self) -> Vec<&ToolInfo> {
576        self.inner.capabilities.all_tools()
577    }
578    
579    /// Gets capability information for a specific tool.
580    pub fn get_tool_info(&self, key: &Key) -> Option<&ToolInfo> {
581        self.inner.capabilities.get_tool(key)
582    }
583}
584
585impl Resolver for ServiceProvider {
586    fn register_disposer<T>(&self, service: Arc<T>)
587    where
588        T: Dispose + 'static,
589    {
590        self.push_sync_disposer(Box::new(move || service.dispose()));
591    }
592
593    fn register_async_disposer<T>(&self, service: Arc<T>)
594    where
595        T: AsyncDispose + 'static,
596    {
597        self.push_async_disposer(Box::new(move || {
598            let service = service.clone();
599            Box::pin(async move { service.dispose().await })
600        }));
601    }
602}