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}