Skip to main content

rust_dix/
entry.rs

1use crate::error::RdiError;
2use crate::key::ServiceKey;
3use crate::provider::ServiceProvider;
4use std::any::{Any, TypeId};
5use std::collections::HashMap;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10/// Service lifetime enum — defines how instances are cached.
11///
12/// - `Transient`: new instance every resolution (never cached)
13/// - `Scoped`: one instance per scope (cached within the scope)
14/// - `Singleton`: one instance shared across all scopes (cached globally)
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum ServiceLifetime {
17    Transient,
18    Scoped,
19    Singleton,
20}
21
22/// Service descriptor holds registration metadata for a service.
23///
24/// Created during `ServiceCollection` building and passed to `ServiceStore`
25/// to create `ServiceEntry`.
26#[derive(Clone)]
27pub struct ServiceDescriptor {
28    pub type_id: TypeId,
29    pub type_name: &'static str,
30    pub key: Option<ServiceKey>,
31    pub factory: ServiceFactory,
32    /// Factory used by `AnyKey` fallback registrations; receives the requested key.
33    pub keyed_factory: Option<KeyedServiceFactory>,
34    pub async_factory: Option<AsyncServiceFactory>,
35    pub lifetime: ServiceLifetime,
36}
37
38/// ServiceStore is the root registry — maps `TypeId` to all `ServiceEntry` entries.
39///
40/// Multiple entries per type exist when keyed variants are registered.
41pub type ServiceStore = HashMap<TypeId, Vec<ServiceEntry>>;
42
43/// Service resolver trait — the core DI resolution interface.
44///
45/// Provides both type-erased and generic resolution methods.
46///
47/// # Panic contract of `Option`-returning lookups
48///
49/// [`Self::get_any`], [`Self::get_keyed_any`], [`Self::get_by_type_id`] and
50/// [`Self::get_keyed_by_type_id`] return `Option` because they answer "found or
51/// not". For a *registered* service that must not be resolved at this point
52/// (root scope validation under `validate_scopes`, a disposed provider/scope,
53/// circular dependency) implementations panic with a **typed [`RdiError`]**
54/// payload instead of returning `None`, so required-resolution APIs
55/// ([`Self::get`], `ServiceProvider::get`, …) can catch it and surface a precise
56/// `Err`. Constructor injection relies on this to propagate the real cause
57/// through user factories.
58///
59/// Infallible probing must use [`Self::try_get_any_result`] /
60/// [`Self::try_get_keyed_any_result`] (or `try_get` / `try_get_keyed`), which
61/// map such failures to `None`/`Err` without panicking.
62pub trait IServiceResolver: Send + Sync {
63    fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>>;
64    fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>>;
65
66    /// Typed-keyed resolution by type name (used by `#[inject(key = <expr>)]`).
67    ///
68    /// The default keeps external implementors source-compatible by falling back
69    /// to the string path **only for string-like keys**
70    /// ([`ServiceKey::as_str`]); typed keys require an implementor override
71    /// (`ServiceProvider`, `Scope`, `ServiceProviderWrapper` and the build-time
72    /// validation resolver all support typed keys).
73    fn get_keyed_any_key(
74        &self,
75        key: &str,
76        variant: &ServiceKey,
77    ) -> Option<Arc<dyn Any + Send + Sync>> {
78        self.get_keyed_any(key, variant.as_str()?)
79    }
80
81    /// Error-reporting typed-keyed lookup (used by required/optional inject
82    /// fields). Defaults to [`Self::get_keyed_any_key`].
83    fn try_get_keyed_any_result_key(
84        &self,
85        key: &str,
86        variant: &ServiceKey,
87    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
88        Ok(self.get_keyed_any_key(key, variant))
89    }
90
91    /// Typed-keyed owned resolution (bypasses caches; non-singleton only).
92    ///
93    /// Default keeps external implementors working for string-like keys.
94    fn get_keyed_owned_any_key(
95        &self,
96        key: &str,
97        variant: &ServiceKey,
98    ) -> Option<Arc<dyn Any + Send + Sync>> {
99        self.get_keyed_owned_any(key, variant.as_str()?)
100    }
101
102    /// Error-reporting typed-keyed owned lookup. Defaults to
103    /// [`Self::get_keyed_owned_any_key`].
104    fn try_get_keyed_owned_any_key(
105        &self,
106        key: &str,
107        variant: &ServiceKey,
108    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
109        Ok(self.get_keyed_owned_any_key(key, variant))
110    }
111
112    /// TypeId 直查(默认 `None`,`ServiceProvider` 等覆盖为 O(1) 直查)。
113    ///
114    /// 外部实现者若需优化可覆盖;不覆盖则 [`Self::get`] 默认实现 fallback
115    /// 到 [`Self::get_any`] 字符串路径。
116    ///
117    /// 覆盖实现须遵循 trait 级 panic 契约(注册但当前不可解析 → typed
118    /// [`RdiError`] payload panic,供 required API 捕获;可选探测请用
119    /// [`Self::try_get_any_result`])。
120    fn get_by_type_id(&self, _tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
121        None
122    }
123
124    /// TypeId 直查 keyed 版本(默认 `None`)。
125    ///
126    /// 覆盖实现须遵循 trait 级 panic 契约(见 [`Self::get_by_type_id`])。
127    fn get_keyed_by_type_id(&self, _tid: TypeId, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
128        None
129    }
130
131    /// Type-erased polymorphic resolution: return all **unkeyed** registered
132    /// instances of the given type, in registration order.
133    ///
134    /// Equivalent to C# MEDI `GetServices<T>()` / `IEnumerable<IFoo>`
135    /// constructor injection, which also excludes keyed services.
136    /// Keyed implementations are enumerated with [`Self::get_all_keyed_any`].
137    ///
138    /// Default empty `Vec` keeps external implementors backward-compatible.
139    fn get_all_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
140        Vec::new()
141    }
142
143    /// Type-erased polymorphic resolution over **keyed** registrations only
144    /// (MEDI `GetKeyedServices<T>(KeyedService.AnyKey)`).
145    ///
146    /// Default empty `Vec` keeps external implementors backward-compatible.
147    fn get_all_keyed_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
148        Vec::new()
149    }
150
151    /// Error-reporting variant of [`Self::get_all_any`], used by the inject
152    /// macros for `Vec<Arc<T>>` fields.
153    ///
154    /// Containers that can fail (scope validation, disposal, factory errors)
155    /// override it so constructor injection surfaces a typed [`RdiError`]
156    /// instead of silently returning a partial/empty list. The default keeps
157    /// external implementors source-compatible.
158    fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
159        Ok(self.get_all_any(key))
160    }
161
162    /// Return an `Arc<ServiceProvider>` for on-demand service resolution.
163    ///
164    /// Used by `#[inject(provider)]` to give a service access to the DI container
165    /// so it can resolve scoped/owned instances on-demand (e.g., per-request
166    /// `DbContext` in a web handler, or per-tick scopes in a singleton worker).
167    ///
168    /// Available during `build()` as well: validation executes singleton
169    /// factories against the real provider, so `#[inject(provider)]` works in
170    /// eagerly built singletons too.
171    ///
172    /// Returns `None` only for resolvers not backed by a `ServiceProvider`.
173    ///
174    /// Default `None` keeps external implementors backward-compatible.
175    fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
176        None
177    }
178
179    /// Resolve a service by type (concrete or `dyn Trait`).
180    /// Returns `Err(RdiError::ServiceNotFound)` if not registered.
181    ///
182    /// 优先走 `get_by_type_id`(O(1) TypeId 直查),未覆盖时 fallback
183    /// 到 `get_any` 字符串路径。
184    fn get<T: ?Sized + Sync + Send + 'static>(&self) -> Result<Arc<T>, RdiError>
185    where
186        Self: Sized,
187    {
188        let type_name = std::any::type_name::<T>();
189        let tid = TypeId::of::<T>();
190        self.get_by_type_id(tid)
191            .or_else(|| self.get_any(type_name))
192            .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
193            .ok_or_else(|| RdiError::not_found(type_name, None))
194    }
195
196    /// Resolve a service by type, returning `None` if it is not registered or
197    /// not resolvable at this point (e.g. a scoped service under root scope
198    /// validation, or a disposed provider/scope).
199    ///
200    /// Uses [`Self::try_get_any_result`]; containers override it with an
201    /// infallible lookup so `try_get` never panics for framework-level failures.
202    fn try_get<T: ?Sized + Sync + Send + 'static>(&self) -> Option<Arc<T>>
203    where
204        Self: Sized,
205    {
206        let type_name = std::any::type_name::<T>();
207        self.try_get_any_result(type_name)
208            .ok()
209            .flatten()
210            .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
211    }
212
213    /// Fallible, type-erased lookup used by [`Self::try_get`].
214    ///
215    /// Default implementation delegates to [`Self::get_any`] (so external
216    /// resolvers keep working); containers that can fail override it so
217    /// infallible lookups can distinguish "absent" from "resolution error"
218    /// without panicking.
219    fn try_get_any_result(
220        &self,
221        key: &str,
222    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
223        Ok(self.get_any(key))
224    }
225
226    /// Resolve a keyed service by type and key. Returns `Err` if not found.
227    fn get_keyed<T: ?Sized + Sync + Send + 'static>(
228        &self,
229        variant: &str,
230    ) -> Result<Arc<T>, RdiError>
231    where
232        Self: Sized,
233    {
234        let type_name = std::any::type_name::<T>();
235        let tid = TypeId::of::<T>();
236        self.get_keyed_by_type_id(tid, variant)
237            .or_else(|| self.get_keyed_any(type_name, variant))
238            .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
239            .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))
240    }
241
242    /// Resolve a keyed service by type and key, returning `None` if not found or
243    /// not resolvable at this point (e.g. root scope validation, disposed
244    /// provider/scope). Never panics for framework-level failures.
245    fn try_get_keyed<T: ?Sized + Sync + Send + 'static>(&self, variant: &str) -> Option<Arc<T>>
246    where
247        Self: Sized,
248    {
249        let type_name = std::any::type_name::<T>();
250        self.try_get_keyed_any_result(type_name, variant)
251            .ok()
252            .flatten()
253            .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
254    }
255
256    /// Fallible, type-erased keyed lookup used by [`Self::try_get_keyed`].
257    ///
258    /// Default implementation delegates to [`Self::get_keyed_any`]; containers
259    /// override it to return `None`/`Err` without panicking.
260    fn try_get_keyed_any_result(
261        &self,
262        key: &str,
263        variant: &str,
264    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
265        Ok(self.get_keyed_any(key, variant))
266    }
267
268    /// Resolve all **unkeyed** registered instances of `T`, in registration
269    /// order.
270    ///
271    /// Equivalent to C# MEDI `GetServices<T>()` (which also excludes keyed
272    /// services). Types implementing `IServiceResolver` get this for free via
273    /// `get_all_any`; types with an inherent `get_all<T>` method (e.g.
274    /// `ServiceProvider`, `Scope`) use that instead — Rust's method resolution
275    /// gives inherent methods priority over trait methods.
276    fn get_all<T: ?Sized + Sync + Send + 'static>(&self) -> Vec<Arc<T>>
277    where
278        Self: Sized,
279    {
280        self.get_all_any(std::any::type_name::<T>())
281            .into_iter()
282            .filter_map(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
283            .collect()
284    }
285
286    /// Type-erased owned resolution: bypass cache, call factory fresh, return
287    /// `Arc<dyn Any + Send + Sync>` (concrete `Arc<Arc<T>>`).
288    ///
289    /// - Singleton → `None` (shared singleton cannot be owned).
290    /// - Scoped/Transient → factory invoked directly (no cache read/write),
291    ///   caller downcasts + `Arc::try_unwrap` to obtain owned `T`.
292    ///
293    /// Default `None` keeps external implementors backward-compatible.
294    fn get_owned_any(&self, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
295        None
296    }
297    fn get_keyed_owned_any(
298        &self,
299        _key: &str,
300        _variant: &str,
301    ) -> Option<Arc<dyn Any + Send + Sync>> {
302        None
303    }
304
305    /// Resolve owned `T` (bypass cache, fresh each call).
306    /// Returns `Err` if not registered or the service is Singleton.
307    fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError>
308    where
309        Self: Sized,
310    {
311        let type_name = std::any::type_name::<T>();
312        let arc_any = self
313            .get_owned_any(type_name)
314            .ok_or_else(|| RdiError::not_found(type_name, None))?;
315        let arc_t: Arc<T> = arc_any
316            .downcast::<Arc<T>>()
317            .ok()
318            .map(|d| Arc::clone(&*d))
319            .ok_or_else(|| RdiError::not_found(type_name, None))?;
320        Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
321    }
322
323    /// Resolve owned `T`, returning `None` if not registered or Singleton.
324    fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T>
325    where
326        Self: Sized,
327    {
328        let arc_any = self.get_owned_any(std::any::type_name::<T>())?;
329        let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
330        Arc::try_unwrap(arc_t).ok()
331    }
332
333    /// Resolve owned keyed `T`.
334    /// Returns `Err` if not found or Singleton.
335    fn get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Result<T, RdiError>
336    where
337        Self: Sized,
338    {
339        let type_name = std::any::type_name::<T>();
340        let arc_any = self
341            .get_keyed_owned_any(type_name, variant)
342            .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
343        let arc_t: Arc<T> = arc_any
344            .downcast::<Arc<T>>()
345            .ok()
346            .map(|d| Arc::clone(&*d))
347            .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
348        Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
349    }
350
351    /// Resolve owned keyed `T`, returning `None` if not found or Singleton.
352    fn try_get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Option<T>
353    where
354        Self: Sized,
355    {
356        let arc_any = self.get_keyed_owned_any(std::any::type_name::<T>(), variant)?;
357        let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
358        Arc::try_unwrap(arc_t).ok()
359    }
360}
361
362/// Typed resolution helpers for factory closures.
363///
364/// Factories are passed `&dyn IServiceResolver`; the generic methods on
365/// [`IServiceResolver`] cannot be called on the trait object, so this extension
366/// restores type safety inside factories (no stringified type names, no manual
367/// `downcast`). Bring it into scope with `use rust_dix::IServiceResolverExt as _;`
368/// (or `use rust_dix::*;`).
369///
370/// Sync factory closures return `Arc<T>`, so `?` belongs in a fallible helper
371/// (or use `.unwrap()`/`.expect()`); the helper result is then wrapped with
372/// [`inject_arc`](crate::inject_arc) to keep the typed error:
373///
374/// ```
375/// use rust_dix::*;
376/// use std::sync::Arc;
377///
378/// struct Config;
379/// struct Handler {
380///     config: Arc<Config>,
381/// }
382///
383/// fn build_handler(resolver: &dyn IServiceResolver) -> Result<Arc<Handler>, RdiError> {
384///     let config: Arc<Config> = resolver.resolve_arc()?; // `?` works here
385///     Ok(Arc::new(Handler { config }))
386/// }
387///
388/// let collection = ServiceCollection::new()
389///     .singleton(|_| Arc::new(Config))
390///     .singleton(|r| inject_arc(build_handler(r)));
391/// let _ = collection;
392/// ```
393pub trait IServiceResolverExt {
394    /// Resolve `T` (concrete or `dyn Trait`) as a shared instance.
395    ///
396    /// Intended for factory closures — the only place where a resolver is seen
397    /// as `&dyn IServiceResolver`. On a container (`ServiceProvider`, `Scope`)
398    /// prefer the inherent `get`/`get_optional`, which are the primary API and
399    /// avoid the extra dyn-aware path.
400    ///
401    /// Returns `Err(RdiError::ServiceNotFound)` when nothing is registered.
402    /// Services that are registered but not resolvable *at this point*
403    /// (strict-scope validation, disposed container/scope, circular dependency)
404    /// are also reported as `Err` — the typed `RdiError` payload of the
405    /// [`IServiceResolver::get_by_type_id`] panic contract is caught and
406    /// converted, so this method never panics for framework-level failures.
407    fn resolve_arc<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError>;
408}
409
410impl<R: IServiceResolver + ?Sized> IServiceResolverExt for R {
411    fn resolve_arc<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError> {
412        let ty = std::any::type_name::<T>();
413        let tid = TypeId::of::<T>();
414        // `get_by_type_id` follows the trait panic contract (registered but not
415        // resolvable → typed `RdiError` payload panic). Catch it here so the
416        // typed API surfaces an ordinary `Err`, matching `IServiceResolver::get`.
417        let found = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
418            self.get_by_type_id(tid).or_else(|| self.get_any(ty))
419        }))
420        .map_err(|payload| RdiError::from_panic_payload(ty, payload))?;
421        found
422            .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
423            .ok_or_else(|| RdiError::not_found(ty, None))
424    }
425}
426
427pub struct ServiceEntry {
428    pub cache_key: usize,
429    pub key: Option<ServiceKey>,
430    pub type_name: &'static str, // kept for IServiceResolver string-based resolution
431    /// Primary sync factory (typed, resolver-only).
432    pub factory: ServiceFactory,
433    /// `AnyKey` fallback factory; receives the requested key.
434    pub keyed_factory: Option<KeyedServiceFactory>,
435    pub async_factory: Option<AsyncServiceFactory>,
436    pub lifetime: ServiceLifetime,
437}
438
439impl ServiceEntry {
440    /// Whether this registration requires async initialization.
441    pub(crate) fn is_async(&self) -> bool {
442        self.async_factory.is_some()
443    }
444
445    /// Whether this is an `AnyKey` fallback registration.
446    pub(crate) fn is_any_key(&self) -> bool {
447        self.key.as_ref().is_some_and(ServiceKey::is_any)
448    }
449}
450
451pub type ServiceFactory =
452    Arc<dyn Fn(&dyn IServiceResolver) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
453
454/// Factory for `AnyKey` fallback registrations: receives the requested key.
455pub type KeyedServiceFactory =
456    Arc<dyn Fn(&dyn IServiceResolver, &ServiceKey) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
457
458/// Async variant of [`ServiceFactory`] — for services that require async
459/// initialization (e.g., database connections, remote config loading).
460///
461/// Used with [`ServiceCollection::build_async`](crate::collection::ServiceCollection::build_async).
462/// The factory receives an `Arc<ServiceProvider>` (instead of `&dyn IServiceResolver`)
463/// because async closures cannot borrow from the caller's stack frame.
464pub type AsyncServiceFactory = Arc<
465    dyn Fn(Arc<ServiceProvider>) -> Pin<Box<dyn Future<Output = Arc<dyn Any + Send + Sync>> + Send>>
466        + Send
467        + Sync,
468>;