ferrous_di/traits/
resolver.rs

1//! Resolver traits for service resolution.
2
3use std::any::TypeId;
4use std::sync::Arc;
5use crate::error::DiResult;
6use crate::key::Key;
7use crate::traits::{Dispose, AsyncDispose};
8use crate::internal::BoxFutureUnit;
9
10/// Core resolver trait for object-safe service resolution.
11///
12/// This trait provides the fundamental service resolution capabilities that are
13/// object-safe (can be used as trait objects). It handles the low-level resolution
14/// mechanics including circular dependency detection through thread-local stacks.
15///
16/// Most users should use the [`Resolver`] trait instead, which provides more
17/// ergonomic generic methods built on top of this trait.
18pub trait ResolverCore: Send + Sync {
19    /// Resolves a single service using thread-local stack for circular dependency detection.
20    ///
21    /// This is the core resolution method that handles circular dependency detection
22    /// and proper lifetime management. Returns the service wrapped in an `Arc` for
23    /// thread-safe sharing.
24    ///
25    /// # Arguments
26    ///
27    /// * `key` - The service key to resolve (type, trait, or multi-trait)
28    ///
29    /// # Returns
30    ///
31    /// * `Ok(AnyArc)` - The resolved service wrapped in `Arc<dyn Any>`
32    /// * `Err(DiError)` - Resolution error (not found, wrong lifetime, circular, etc.)
33    fn resolve_any(&self, key: &Key) -> DiResult<Arc<dyn std::any::Any + Send + Sync>>;
34    
35    /// Resolves all multi-bound services for a trait using circular dependency detection.
36    ///
37    /// For traits registered with multiple implementations, this returns all of them
38    /// in registration order. Single-bound traits and concrete types return empty vectors.
39    ///
40    /// # Arguments
41    ///
42    /// * `key` - The service key to resolve (typically a trait key)
43    ///
44    /// # Returns
45    ///
46    /// * `Ok(Vec<AnyArc>)` - All resolved implementations as `Arc<dyn Any>`
47    /// * `Err(DiError)` - Resolution error for any implementation
48    fn resolve_many(&self, key: &Key) -> DiResult<Vec<Arc<dyn std::any::Any + Send + Sync>>>;
49    
50    /// Legacy internal resolve method for compatibility.
51    ///
52    /// Delegates to [`resolve_any`](Self::resolve_any) for backward compatibility.
53    fn resolve_any_internal(&self, key: &Key) -> DiResult<Arc<dyn std::any::Any + Send + Sync>> {
54        self.resolve_any(key)
55    }
56    
57    /// Legacy internal resolve many method for compatibility.
58    ///
59    /// Delegates to [`resolve_many`](Self::resolve_many) for backward compatibility.
60    fn resolve_many_internal(&self, key: &Key) -> DiResult<Vec<Arc<dyn std::any::Any + Send + Sync>>> {
61        self.resolve_many(key)
62    }
63
64    /// Registers a synchronous disposal hook.
65    ///
66    /// Used internally by factories to register disposal callbacks that will be
67    /// executed when the containing scope or provider is disposed.
68    fn push_sync_disposer(&self, f: Box<dyn FnOnce() + Send>);
69
70    /// Registers an asynchronous disposal hook.
71    ///
72    /// Used internally by factories to register async disposal callbacks that will be
73    /// executed when the containing scope or provider is disposed.
74    fn push_async_disposer(&self, f: Box<dyn FnOnce() -> BoxFutureUnit + Send>);
75}
76
77/// High-level resolver interface with generic methods for type-safe service resolution.
78///
79/// This trait provides the main API that users interact with for resolving services.
80/// It builds on [`ResolverCore`] to offer type-safe generic methods that handle
81/// the complexities of type erasure and casting internally.
82///
83/// Both `ServiceProvider` and `Scope` implement this trait, making them
84/// interchangeable for service resolution within their respective contexts.
85///
86/// # Examples
87///
88/// ```
89/// use ferrous_di::{ServiceCollection, Resolver};
90/// use std::sync::Arc;
91///
92/// trait Logger: Send + Sync {
93///     fn log(&self, msg: &str);
94/// }
95///
96/// struct ConsoleLogger;
97/// impl Logger for ConsoleLogger {
98///     fn log(&self, msg: &str) {
99///         println!("LOG: {}", msg);
100///     }
101/// }
102///
103/// let mut collection = ServiceCollection::new();
104/// collection.add_singleton(42usize);
105/// collection.add_singleton_trait(Arc::new(ConsoleLogger) as Arc<dyn Logger>);
106///
107/// let provider = collection.build();
108///
109/// // Resolve concrete types
110/// let number = provider.get_required::<usize>();
111/// assert_eq!(*number, 42);
112///
113/// // Resolve trait objects
114/// let logger = provider.get_required_trait::<dyn Logger>();
115/// logger.log("Service resolved successfully");
116/// ```
117pub trait Resolver: ResolverCore {
118    /// Resolves a concrete service type.
119    ///
120    /// Returns the service instance wrapped in an `Arc` for thread-safe sharing.
121    /// The service must be registered with the exact type `T`.
122    ///
123    /// # Type Parameters
124    ///
125    /// * `T` - The concrete service type to resolve
126    ///
127    /// # Returns
128    ///
129    /// * `Ok(Arc<T>)` - The resolved service instance
130    /// * `Err(DiError)` - Resolution error (not found, wrong lifetime, circular, etc.)
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use ferrous_di::{ServiceCollection, Resolver};
136    ///
137    /// let mut collection = ServiceCollection::new();
138    /// collection.add_singleton("configuration".to_string());
139    ///
140    /// let provider = collection.build();
141    /// let config = provider.get::<String>().unwrap();
142    /// assert_eq!(&*config, "configuration");
143    /// ```
144    fn get<T: 'static + Send + Sync>(&self) -> DiResult<Arc<T>> {
145        let key = Key::Type(TypeId::of::<T>(), std::any::type_name::<T>());
146        let any = self.resolve_any_internal(&key)?;
147        any.downcast::<T>()
148            .map_err(|_| crate::error::DiError::TypeMismatch(std::any::type_name::<T>()))
149    }
150    
151    /// Resolves a single trait implementation.
152    ///
153    /// Returns the most recently registered implementation for the trait `T`.
154    /// If multiple implementations are registered, this returns the last one.
155    /// For accessing all implementations, use [`get_all_trait`](Self::get_all_trait).
156    ///
157    /// # Type Parameters
158    ///
159    /// * `T` - The trait type to resolve (can be unsized with `?Sized`)
160    ///
161    /// # Returns
162    ///
163    /// * `Ok(Arc<T>)` - The resolved trait implementation
164    /// * `Err(DiError)` - Resolution error (not found, wrong lifetime, circular, etc.)
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use ferrous_di::{ServiceCollection, Resolver};
170    /// use std::sync::Arc;
171    ///
172    /// trait Database: Send + Sync {
173    ///     fn connect(&self) -> &str;
174    /// }
175    ///
176    /// struct PostgresDb;
177    /// impl Database for PostgresDb {
178    ///     fn connect(&self) -> &str { "postgres://..." }
179    /// }
180    ///
181    /// let mut collection = ServiceCollection::new();
182    /// collection.add_singleton_trait(Arc::new(PostgresDb) as Arc<dyn Database>);
183    ///
184    /// let provider = collection.build();
185    /// let db = provider.get_trait::<dyn Database>().unwrap();
186    /// assert_eq!(db.connect(), "postgres://...");
187    /// ```
188    fn get_trait<T: ?Sized + 'static + Send + Sync>(&self) -> DiResult<Arc<T>> 
189    where
190        Arc<T>: 'static,
191    {
192        let key = Key::Trait(std::any::type_name::<T>());
193        let any = self.resolve_any_internal(&key)?;
194        // Expert fix: Handle Arc<Arc<dyn Trait>> storage pattern
195        any.downcast::<Arc<T>>()
196            .map(|boxed| (*boxed).clone())
197            .map_err(|_| crate::error::DiError::TypeMismatch(std::any::type_name::<T>()))
198    }
199    
200    /// Resolves all registered implementations of a trait.
201    ///
202    /// Returns all implementations registered for trait `T` in the order they
203    /// were registered. This is useful for collecting all implementations of
204    /// a plugin interface or service collection pattern.
205    ///
206    /// # Type Parameters
207    ///
208    /// * `T` - The trait type to resolve all implementations for
209    ///
210    /// # Returns
211    ///
212    /// * `Ok(Vec<Arc<T>>)` - All registered trait implementations
213    /// * `Err(DiError)` - Resolution error for any implementation
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use ferrous_di::{ServiceCollection, Resolver};
219    /// use std::sync::Arc;
220    ///
221    /// trait Plugin: Send + Sync {
222    ///     fn name(&self) -> &str;
223    /// }
224    ///
225    /// struct PluginA;
226    /// impl Plugin for PluginA {
227    ///     fn name(&self) -> &str { "Plugin A" }
228    /// }
229    ///
230    /// struct PluginB;
231    /// impl Plugin for PluginB {
232    ///     fn name(&self) -> &str { "Plugin B" }
233    /// }
234    ///
235    /// let mut collection = ServiceCollection::new();
236    /// collection.add_trait_implementation(Arc::new(PluginA) as Arc<dyn Plugin>, ferrous_di::Lifetime::Singleton);
237    /// collection.add_trait_implementation(Arc::new(PluginB) as Arc<dyn Plugin>, ferrous_di::Lifetime::Singleton);
238    ///
239    /// let provider = collection.build();
240    /// let plugins = provider.get_all_trait::<dyn Plugin>().unwrap();
241    /// assert_eq!(plugins.len(), 2);
242    /// assert_eq!(plugins[0].name(), "Plugin A");
243    /// assert_eq!(plugins[1].name(), "Plugin B");
244    /// ```
245    fn get_all_trait<T: ?Sized + 'static + Send + Sync>(&self) -> DiResult<Vec<Arc<T>>>
246    where
247        Arc<T>: 'static,
248    {
249        let key = Key::Trait(std::any::type_name::<T>());
250        let anys = self.resolve_many_internal(&key)?;
251        
252        let mut results = Vec::with_capacity(anys.len());
253        for any in anys {
254            // Expert fix: Handle Arc<Arc<dyn Trait>> storage pattern
255            let arc = any.downcast::<Arc<T>>()
256                .map(|boxed| (*boxed).clone())
257                .map_err(|_| crate::error::DiError::TypeMismatch(std::any::type_name::<T>()))?;
258            results.push(arc);
259        }
260        Ok(results)
261    }
262    
263    /// Resolves a concrete service type, panicking on failure.
264    ///
265    /// This is a convenience method that calls [`get`](Self::get) and panics if
266    /// the service cannot be resolved. Use this when you're certain the service
267    /// is registered and want to fail fast on configuration errors.
268    ///
269    /// # Type Parameters
270    ///
271    /// * `T` - The concrete service type to resolve
272    ///
273    /// # Returns
274    ///
275    /// * `Arc<T>` - The resolved service instance
276    ///
277    /// # Panics
278    ///
279    /// Panics if the service cannot be resolved (not found, wrong lifetime,
280    /// circular dependency, etc.).
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use ferrous_di::{ServiceCollection, Resolver};
286    ///
287    /// let mut collection = ServiceCollection::new();
288    /// collection.add_singleton(42usize);
289    ///
290    /// let provider = collection.build();
291    /// let number = provider.get_required::<usize>(); // Will panic if not found
292    /// assert_eq!(*number, 42);
293    /// ```
294    fn get_required<T: 'static + Send + Sync>(&self) -> Arc<T> {
295        self.get::<T>()
296            .unwrap_or_else(|e| panic!("Failed to resolve {}: {:?}", std::any::type_name::<T>(), e))
297    }
298    
299    /// Resolves a trait implementation, panicking on failure.
300    ///
301    /// This is a convenience method that calls [`get_trait`](Self::get_trait) and panics
302    /// if the trait cannot be resolved. Use this when you're certain the trait is
303    /// registered and want to fail fast on configuration errors.
304    ///
305    /// # Type Parameters
306    ///
307    /// * `T` - The trait type to resolve
308    ///
309    /// # Returns
310    ///
311    /// * `Arc<T>` - The resolved trait implementation
312    ///
313    /// # Panics
314    ///
315    /// Panics if the trait cannot be resolved (not found, wrong lifetime,
316    /// circular dependency, etc.).
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// use ferrous_di::{ServiceCollection, Resolver};
322    /// use std::sync::Arc;
323    ///
324    /// trait Cache: Send + Sync {
325    ///     fn get(&self, key: &str) -> Option<String>;
326    /// }
327    ///
328    /// struct MemoryCache;
329    /// impl Cache for MemoryCache {
330    ///     fn get(&self, _key: &str) -> Option<String> {
331    ///         Some("cached_value".to_string())
332    ///     }
333    /// }
334    ///
335    /// let mut collection = ServiceCollection::new();
336    /// collection.add_singleton_trait(Arc::new(MemoryCache) as Arc<dyn Cache>);
337    ///
338    /// let provider = collection.build();
339    /// let cache = provider.get_required_trait::<dyn Cache>(); // Will panic if not found
340    /// assert_eq!(cache.get("key"), Some("cached_value".to_string()));
341    /// ```
342    fn get_required_trait<T: ?Sized + 'static + Send + Sync>(&self) -> Arc<T>
343    where
344        Arc<T>: 'static,
345    {
346        self.get_trait::<T>()
347            .unwrap_or_else(|e| panic!("Failed to resolve trait {}: {:?}", std::any::type_name::<T>(), e))
348    }
349
350    /// Registers a service for synchronous disposal.
351    ///
352    /// This method should be called from service factories to ensure proper cleanup
353    /// when the containing scope or provider is disposed. Disposal hooks execute
354    /// in LIFO order (last registered, first disposed).
355    ///
356    /// # Arguments
357    ///
358    /// * `service` - The service instance to register for disposal
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// use ferrous_di::{Dispose, ServiceCollection, Resolver};
364    /// use std::sync::Arc;
365    ///
366    /// struct Cache {
367    ///     name: String,
368    /// }
369    ///
370    /// impl Dispose for Cache {
371    ///     fn dispose(&self) {
372    ///         println!("Disposing cache: {}", self.name);
373    ///     }
374    /// }
375    ///
376    /// let mut services = ServiceCollection::new();
377    /// services.add_scoped_factory::<Cache, _>(|resolver| {
378    ///     let cache = Arc::new(Cache { name: "user_cache".to_string() });
379    ///     resolver.register_disposer(cache.clone());
380    ///     Cache { name: "user_cache".to_string() }
381    /// });
382    /// ```
383    fn register_disposer<T: Dispose>(&self, service: Arc<T>) {
384        self.push_sync_disposer(Box::new(move || service.dispose()));
385    }
386
387    /// Registers a service for asynchronous disposal.
388    ///
389    /// This method should be called from service factories to ensure proper async cleanup
390    /// when the containing scope or provider is disposed. Async disposal hooks execute
391    /// before sync hooks, in LIFO order (last registered, first disposed).
392    ///
393    /// # Arguments
394    ///
395    /// * `service` - The service instance to register for async disposal
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// use ferrous_di::{AsyncDispose, ServiceCollection, Resolver};
401    /// use async_trait::async_trait;
402    /// use std::sync::Arc;
403    ///
404    /// struct DbConnection {
405    ///     id: String,
406    /// }
407    ///
408    /// #[async_trait]
409    /// impl AsyncDispose for DbConnection {
410    ///     async fn dispose(&self) {
411    ///         println!("Closing connection: {}", self.id);
412    ///         // Async cleanup...
413    ///     }
414    /// }
415    ///
416    /// let mut services = ServiceCollection::new();
417    /// services.add_singleton_factory::<DbConnection, _>(|resolver| {
418    ///     let conn = Arc::new(DbConnection { id: "conn_1".to_string() });
419    ///     resolver.register_async_disposer(conn.clone());
420    ///     DbConnection { id: "conn_1".to_string() }
421    /// });
422    /// ```
423    fn register_async_disposer<T: AsyncDispose>(&self, service: Arc<T>) {
424        self.push_async_disposer(Box::new(move || Box::pin(async move {
425            service.dispose().await;
426        })));
427    }
428    
429    // Named service resolution methods continue...
430    
431    /// Resolves a named concrete service type.
432    ///
433    /// Returns the service instance registered with the given name and type `T`.
434    ///
435    /// # Type Parameters
436    ///
437    /// * `T` - The concrete service type to resolve
438    ///
439    /// # Arguments
440    ///
441    /// * `name` - The service name to resolve
442    ///
443    /// # Returns
444    ///
445    /// * `Ok(Arc<T>)` - The resolved named service instance
446    /// * `Err(DiError)` - Resolution error (not found, wrong lifetime, circular, etc.)
447    fn get_named<T: 'static + Send + Sync>(&self, name: &'static str) -> DiResult<Arc<T>> {
448        let key = Key::TypeNamed(TypeId::of::<T>(), std::any::type_name::<T>(), name);
449        let any = self.resolve_any_internal(&key)?;
450        any.downcast::<T>()
451            .map_err(|_| crate::error::DiError::TypeMismatch(std::any::type_name::<T>()))
452    }
453    
454    /// Resolves a named concrete service type, panicking on failure.
455    fn get_named_required<T: 'static + Send + Sync>(&self, name: &'static str) -> Arc<T> {
456        self.get_named::<T>(name)
457            .unwrap_or_else(|e| panic!("Failed to resolve named {} ({}): {:?}", std::any::type_name::<T>(), name, e))
458    }
459    
460    /// Resolves a named trait implementation.
461    fn get_named_trait<T: ?Sized + 'static + Send + Sync>(&self, name: &'static str) -> DiResult<Arc<T>>
462    where
463        Arc<T>: 'static,
464    {
465        let key = Key::TraitNamed(std::any::type_name::<T>(), name);
466        let any = self.resolve_any_internal(&key)?;
467        any.downcast::<Arc<T>>()
468            .map(|boxed| (*boxed).clone())
469            .map_err(|_| crate::error::DiError::TypeMismatch(std::any::type_name::<T>()))
470    }
471    
472    /// Resolves a named trait implementation, panicking on failure.
473    fn get_named_trait_required<T: ?Sized + 'static + Send + Sync>(&self, name: &'static str) -> Arc<T>
474    where
475        Arc<T>: 'static,
476    {
477        self.get_named_trait::<T>(name)
478            .unwrap_or_else(|e| panic!("Failed to resolve named trait {} ({}): {:?}", std::any::type_name::<T>(), name, e))
479    }
480}