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 /// TypeId 直查(默认 `None`,`ServiceProvider` 等覆盖为 O(1) 直查)。
67 ///
68 /// 外部实现者若需优化可覆盖;不覆盖则 [`Self::get`] 默认实现 fallback
69 /// 到 [`Self::get_any`] 字符串路径。
70 ///
71 /// 覆盖实现须遵循 trait 级 panic 契约(注册但当前不可解析 → typed
72 /// [`RdiError`] payload panic,供 required API 捕获;可选探测请用
73 /// [`Self::try_get_any_result`])。
74 fn get_by_type_id(&self, _tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
75 None
76 }
77 /// TypeId 直查 keyed 版本(默认 `None`)。
78 ///
79 /// 覆盖实现须遵循 trait 级 panic 契约(见 [`Self::get_by_type_id`])。
80 fn get_keyed_by_type_id(&self, _tid: TypeId, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
81 None
82 }
83
84 /// Type-erased polymorphic resolution: return all **unkeyed** registered
85 /// instances of the given type, in registration order.
86 ///
87 /// Equivalent to C# MEDI `GetServices<T>()` / `IEnumerable<IFoo>`
88 /// constructor injection, which also excludes keyed services.
89 /// Keyed implementations are enumerated with [`Self::get_all_keyed_any`].
90 ///
91 /// Default empty `Vec` keeps external implementors backward-compatible.
92 fn get_all_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
93 Vec::new()
94 }
95
96 /// Type-erased polymorphic resolution over **keyed** registrations only
97 /// (MEDI `GetKeyedServices<T>(KeyedService.AnyKey)`).
98 ///
99 /// Default empty `Vec` keeps external implementors backward-compatible.
100 fn get_all_keyed_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
101 Vec::new()
102 }
103
104 /// Error-reporting variant of [`Self::get_all_any`], used by the inject
105 /// macros for `Vec<Arc<T>>` fields.
106 ///
107 /// Containers that can fail (scope validation, disposal, factory errors)
108 /// override it so constructor injection surfaces a typed [`RdiError`]
109 /// instead of silently returning a partial/empty list. The default keeps
110 /// external implementors source-compatible.
111 fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
112 Ok(self.get_all_any(key))
113 }
114
115 /// Return an `Arc<ServiceProvider>` for on-demand service resolution.
116 ///
117 /// Used by `#[inject(provider)]` to give a service access to the DI container
118 /// so it can resolve scoped/owned instances on-demand (e.g., per-request
119 /// `DbContext` in a web handler, or per-tick scopes in a singleton worker).
120 ///
121 /// Available during `build()` as well: validation executes singleton
122 /// factories against the real provider, so `#[inject(provider)]` works in
123 /// eagerly built singletons too.
124 ///
125 /// Returns `None` only for resolvers not backed by a `ServiceProvider`.
126 ///
127 /// Default `None` keeps external implementors backward-compatible.
128 fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
129 None
130 }
131
132 /// Resolve a service by type (concrete or `dyn Trait`).
133 /// Returns `Err(RdiError::ServiceNotFound)` if not registered.
134 ///
135 /// 优先走 `get_by_type_id`(O(1) TypeId 直查),未覆盖时 fallback
136 /// 到 `get_any` 字符串路径。
137 fn get<T: ?Sized + Sync + Send + 'static>(&self) -> Result<Arc<T>, RdiError>
138 where
139 Self: Sized,
140 {
141 let type_name = std::any::type_name::<T>();
142 let tid = TypeId::of::<T>();
143 self.get_by_type_id(tid)
144 .or_else(|| self.get_any(type_name))
145 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
146 .ok_or_else(|| RdiError::not_found(type_name, None))
147 }
148
149 /// Resolve a service by type, returning `None` if it is not registered or
150 /// not resolvable at this point (e.g. a scoped service under root scope
151 /// validation, or a disposed provider/scope).
152 ///
153 /// Uses [`Self::try_get_any_result`]; containers override it with an
154 /// infallible lookup so `try_get` never panics for framework-level failures.
155 fn try_get<T: ?Sized + Sync + Send + 'static>(&self) -> Option<Arc<T>>
156 where
157 Self: Sized,
158 {
159 let type_name = std::any::type_name::<T>();
160 self.try_get_any_result(type_name)
161 .ok()
162 .flatten()
163 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
164 }
165
166 /// Fallible, type-erased lookup used by [`Self::try_get`].
167 ///
168 /// Default implementation delegates to [`Self::get_any`] (so external
169 /// resolvers keep working); containers that can fail override it so
170 /// infallible lookups can distinguish "absent" from "resolution error"
171 /// without panicking.
172 fn try_get_any_result(
173 &self,
174 key: &str,
175 ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
176 Ok(self.get_any(key))
177 }
178
179 /// Resolve a keyed service by type and key. Returns `Err` if not found.
180 fn get_keyed<T: ?Sized + Sync + Send + 'static>(
181 &self,
182 variant: &str,
183 ) -> Result<Arc<T>, RdiError>
184 where
185 Self: Sized,
186 {
187 let type_name = std::any::type_name::<T>();
188 let tid = TypeId::of::<T>();
189 self.get_keyed_by_type_id(tid, variant)
190 .or_else(|| self.get_keyed_any(type_name, variant))
191 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
192 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))
193 }
194
195 /// Resolve a keyed service by type and key, returning `None` if not found or
196 /// not resolvable at this point (e.g. root scope validation, disposed
197 /// provider/scope). Never panics for framework-level failures.
198 fn try_get_keyed<T: ?Sized + Sync + Send + 'static>(&self, variant: &str) -> Option<Arc<T>>
199 where
200 Self: Sized,
201 {
202 let type_name = std::any::type_name::<T>();
203 self.try_get_keyed_any_result(type_name, variant)
204 .ok()
205 .flatten()
206 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
207 }
208
209 /// Fallible, type-erased keyed lookup used by [`Self::try_get_keyed`].
210 ///
211 /// Default implementation delegates to [`Self::get_keyed_any`]; containers
212 /// override it to return `None`/`Err` without panicking.
213 fn try_get_keyed_any_result(
214 &self,
215 key: &str,
216 variant: &str,
217 ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
218 Ok(self.get_keyed_any(key, variant))
219 }
220
221 /// Resolve all **unkeyed** registered instances of `T`, in registration
222 /// order.
223 ///
224 /// Equivalent to C# MEDI `GetServices<T>()` (which also excludes keyed
225 /// services). Types implementing `IServiceResolver` get this for free via
226 /// `get_all_any`; types with an inherent `get_all<T>` method (e.g.
227 /// `ServiceProvider`, `Scope`) use that instead — Rust's method resolution
228 /// gives inherent methods priority over trait methods.
229 fn get_all<T: ?Sized + Sync + Send + 'static>(&self) -> Vec<Arc<T>>
230 where
231 Self: Sized,
232 {
233 self.get_all_any(std::any::type_name::<T>())
234 .into_iter()
235 .filter_map(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
236 .collect()
237 }
238
239 /// Type-erased owned resolution: bypass cache, call factory fresh, return
240 /// `Arc<dyn Any + Send + Sync>` (concrete `Arc<Arc<T>>`).
241 ///
242 /// - Singleton → `None` (shared singleton cannot be owned).
243 /// - Scoped/Transient → factory invoked directly (no cache read/write),
244 /// caller downcasts + `Arc::try_unwrap` to obtain owned `T`.
245 ///
246 /// Default `None` keeps external implementors backward-compatible.
247 fn get_owned_any(&self, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
248 None
249 }
250 fn get_keyed_owned_any(
251 &self,
252 _key: &str,
253 _variant: &str,
254 ) -> Option<Arc<dyn Any + Send + Sync>> {
255 None
256 }
257
258 /// Resolve owned `T` (bypass cache, fresh each call).
259 /// Returns `Err` if not registered or the service is Singleton.
260 fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError>
261 where
262 Self: Sized,
263 {
264 let type_name = std::any::type_name::<T>();
265 let arc_any = self
266 .get_owned_any(type_name)
267 .ok_or_else(|| RdiError::not_found(type_name, None))?;
268 let arc_t: Arc<T> = arc_any
269 .downcast::<Arc<T>>()
270 .ok()
271 .map(|d| Arc::clone(&*d))
272 .ok_or_else(|| RdiError::not_found(type_name, None))?;
273 Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
274 }
275
276 /// Resolve owned `T`, returning `None` if not registered or Singleton.
277 fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T>
278 where
279 Self: Sized,
280 {
281 let arc_any = self.get_owned_any(std::any::type_name::<T>())?;
282 let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
283 Arc::try_unwrap(arc_t).ok()
284 }
285
286 /// Resolve owned keyed `T`.
287 /// Returns `Err` if not found or Singleton.
288 fn get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Result<T, RdiError>
289 where
290 Self: Sized,
291 {
292 let type_name = std::any::type_name::<T>();
293 let arc_any = self
294 .get_keyed_owned_any(type_name, variant)
295 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
296 let arc_t: Arc<T> = arc_any
297 .downcast::<Arc<T>>()
298 .ok()
299 .map(|d| Arc::clone(&*d))
300 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
301 Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
302 }
303
304 /// Resolve owned keyed `T`, returning `None` if not found or Singleton.
305 fn try_get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Option<T>
306 where
307 Self: Sized,
308 {
309 let arc_any = self.get_keyed_owned_any(std::any::type_name::<T>(), variant)?;
310 let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
311 Arc::try_unwrap(arc_t).ok()
312 }
313}
314
315pub struct ServiceEntry {
316 pub cache_key: usize,
317 pub key: Option<ServiceKey>,
318 pub type_name: &'static str, // kept for IServiceResolver string-based resolution
319 /// Primary sync factory (typed, resolver-only).
320 pub factory: ServiceFactory,
321 /// `AnyKey` fallback factory; receives the requested key.
322 pub keyed_factory: Option<KeyedServiceFactory>,
323 pub async_factory: Option<AsyncServiceFactory>,
324 pub lifetime: ServiceLifetime,
325}
326
327impl ServiceEntry {
328 /// Whether this registration requires async initialization.
329 pub(crate) fn is_async(&self) -> bool {
330 self.async_factory.is_some()
331 }
332
333 /// Whether this is an `AnyKey` fallback registration.
334 pub(crate) fn is_any_key(&self) -> bool {
335 self.key.as_ref().is_some_and(ServiceKey::is_any)
336 }
337}
338
339pub type ServiceFactory =
340 Arc<dyn Fn(&dyn IServiceResolver) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
341
342/// Factory for `AnyKey` fallback registrations: receives the requested key.
343pub type KeyedServiceFactory =
344 Arc<dyn Fn(&dyn IServiceResolver, &ServiceKey) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
345
346/// Async variant of [`ServiceFactory`] — for services that require async
347/// initialization (e.g., database connections, remote config loading).
348///
349/// Used with [`ServiceCollection::build_async`](crate::collection::ServiceCollection::build_async).
350/// The factory receives an `Arc<ServiceProvider>` (instead of `&dyn IServiceResolver`)
351/// because async closures cannot borrow from the caller's stack frame.
352pub type AsyncServiceFactory = Arc<
353 dyn Fn(Arc<ServiceProvider>) -> Pin<Box<dyn Future<Output = Arc<dyn Any + Send + Sync>> + Send>>
354 + Send
355 + Sync,
356>;