rust_dix/entry.rs
1use crate::error::RdiError;
2use crate::provider::ServiceProvider;
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8
9/// Service lifetime enum — defines how instances are cached.
10///
11/// - `Transient`: new instance every resolution (never cached)
12/// - `Scoped`: one instance per scope (cached within the scope)
13/// - `Singleton`: one instance shared across all scopes (cached globally)
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum ServiceLifetime {
16 Transient,
17 Scoped,
18 Singleton,
19}
20
21/// Service descriptor holds registration metadata for a service.
22///
23/// Created during `ServiceCollection` building and passed to `ServiceStore`
24/// to create `ServiceEntry`.
25#[derive(Clone)]
26pub struct ServiceDescriptor {
27 pub type_id: TypeId,
28 pub type_name: &'static str,
29 pub key: Option<String>,
30 pub factory: ServiceFactory,
31 pub async_factory: Option<AsyncServiceFactory>,
32 pub lifetime: ServiceLifetime,
33}
34
35/// ServiceStore is the root registry — maps `TypeId` to all `ServiceEntry` entries.
36///
37/// Multiple entries per type exist when keyed variants are registered.
38pub type ServiceStore = HashMap<TypeId, Vec<ServiceEntry>>;
39
40/// Service resolver trait — the core DI resolution interface.
41///
42/// Provides both type-erased and generic resolution methods.
43///
44/// # Panic contract of `Option`-returning lookups
45///
46/// [`Self::get_any`], [`Self::get_keyed_any`], [`Self::get_by_type_id`] and
47/// [`Self::get_keyed_by_type_id`] return `Option` because they answer "found or
48/// not". For a *registered* service that must not be resolved at this point
49/// (root scope validation under `validate_scopes`, a disposed provider/scope,
50/// circular dependency) implementations panic with a **typed [`RdiError`]**
51/// payload instead of returning `None`, so required-resolution APIs
52/// ([`Self::get`], `ServiceProvider::get`, …) can catch it and surface a precise
53/// `Err`. Constructor injection relies on this to propagate the real cause
54/// through user factories.
55///
56/// Infallible probing must use [`Self::try_get_any_result`] /
57/// [`Self::try_get_keyed_any_result`] (or `try_get` / `try_get_keyed`), which
58/// map such failures to `None`/`Err` without panicking.
59pub trait IServiceResolver: Send + Sync {
60 fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>>;
61 fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>>;
62
63 /// TypeId 直查(默认 `None`,`ServiceProvider` 等覆盖为 O(1) 直查)。
64 ///
65 /// 外部实现者若需优化可覆盖;不覆盖则 [`Self::get`] 默认实现 fallback
66 /// 到 [`Self::get_any`] 字符串路径。
67 ///
68 /// 覆盖实现须遵循 trait 级 panic 契约(注册但当前不可解析 → typed
69 /// [`RdiError`] payload panic,供 required API 捕获;可选探测请用
70 /// [`Self::try_get_any_result`])。
71 fn get_by_type_id(&self, _tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
72 None
73 }
74 /// TypeId 直查 keyed 版本(默认 `None`)。
75 ///
76 /// 覆盖实现须遵循 trait 级 panic 契约(见 [`Self::get_by_type_id`])。
77 fn get_keyed_by_type_id(&self, _tid: TypeId, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
78 None
79 }
80
81 /// Type-erased polymorphic resolution: return all **unkeyed** registered
82 /// instances of the given type, in registration order.
83 ///
84 /// Equivalent to C# MEDI `GetServices<T>()` / `IEnumerable<IFoo>`
85 /// constructor injection, which also excludes keyed services.
86 /// Keyed implementations are enumerated with [`Self::get_all_keyed_any`].
87 ///
88 /// Default empty `Vec` keeps external implementors backward-compatible.
89 fn get_all_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
90 Vec::new()
91 }
92
93 /// Type-erased polymorphic resolution over **keyed** registrations only
94 /// (MEDI `GetKeyedServices<T>(KeyedService.AnyKey)`).
95 ///
96 /// Default empty `Vec` keeps external implementors backward-compatible.
97 fn get_all_keyed_any(&self, _key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
98 Vec::new()
99 }
100
101 /// Error-reporting variant of [`Self::get_all_any`], used by the inject
102 /// macros for `Vec<Arc<T>>` fields.
103 ///
104 /// Containers that can fail (scope validation, disposal, factory errors)
105 /// override it so constructor injection surfaces a typed [`RdiError`]
106 /// instead of silently returning a partial/empty list. The default keeps
107 /// external implementors source-compatible.
108 fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
109 Ok(self.get_all_any(key))
110 }
111
112 /// Return an `Arc<ServiceProvider>` for on-demand service resolution.
113 ///
114 /// Used by `#[inject(provider)]` to give a service access to the DI container
115 /// so it can resolve scoped/owned instances on-demand (e.g., per-request
116 /// `DbContext` in a web handler, or per-tick scopes in a singleton worker).
117 ///
118 /// Available during `build()` as well: validation executes singleton
119 /// factories against the real provider, so `#[inject(provider)]` works in
120 /// eagerly built singletons too.
121 ///
122 /// Returns `None` only for resolvers not backed by a `ServiceProvider`.
123 ///
124 /// Default `None` keeps external implementors backward-compatible.
125 fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
126 None
127 }
128
129 /// Resolve a service by type (concrete or `dyn Trait`).
130 /// Returns `Err(RdiError::ServiceNotFound)` if not registered.
131 ///
132 /// 优先走 `get_by_type_id`(O(1) TypeId 直查),未覆盖时 fallback
133 /// 到 `get_any` 字符串路径。
134 fn get<T: ?Sized + Sync + Send + 'static>(&self) -> Result<Arc<T>, RdiError>
135 where
136 Self: Sized,
137 {
138 let type_name = std::any::type_name::<T>();
139 let tid = TypeId::of::<T>();
140 self.get_by_type_id(tid)
141 .or_else(|| self.get_any(type_name))
142 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
143 .ok_or_else(|| RdiError::not_found(type_name, None))
144 }
145
146 /// Resolve a service by type, returning `None` if it is not registered or
147 /// not resolvable at this point (e.g. a scoped service under root scope
148 /// validation, or a disposed provider/scope).
149 ///
150 /// Uses [`Self::try_get_any_result`]; containers override it with an
151 /// infallible lookup so `try_get` never panics for framework-level failures.
152 fn try_get<T: ?Sized + Sync + Send + 'static>(&self) -> Option<Arc<T>>
153 where
154 Self: Sized,
155 {
156 let type_name = std::any::type_name::<T>();
157 self.try_get_any_result(type_name)
158 .ok()
159 .flatten()
160 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
161 }
162
163 /// Fallible, type-erased lookup used by [`Self::try_get`].
164 ///
165 /// Default implementation delegates to [`Self::get_any`] (so external
166 /// resolvers keep working); containers that can fail override it so
167 /// infallible lookups can distinguish "absent" from "resolution error"
168 /// without panicking.
169 fn try_get_any_result(
170 &self,
171 key: &str,
172 ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
173 Ok(self.get_any(key))
174 }
175
176 /// Resolve a keyed service by type and key. Returns `Err` if not found.
177 fn get_keyed<T: ?Sized + Sync + Send + 'static>(
178 &self,
179 variant: &str,
180 ) -> Result<Arc<T>, RdiError>
181 where
182 Self: Sized,
183 {
184 let type_name = std::any::type_name::<T>();
185 let tid = TypeId::of::<T>();
186 self.get_keyed_by_type_id(tid, variant)
187 .or_else(|| self.get_keyed_any(type_name, variant))
188 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
189 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))
190 }
191
192 /// Resolve a keyed service by type and key, returning `None` if not found or
193 /// not resolvable at this point (e.g. root scope validation, disposed
194 /// provider/scope). Never panics for framework-level failures.
195 fn try_get_keyed<T: ?Sized + Sync + Send + 'static>(&self, variant: &str) -> Option<Arc<T>>
196 where
197 Self: Sized,
198 {
199 let type_name = std::any::type_name::<T>();
200 self.try_get_keyed_any_result(type_name, variant)
201 .ok()
202 .flatten()
203 .and_then(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
204 }
205
206 /// Fallible, type-erased keyed lookup used by [`Self::try_get_keyed`].
207 ///
208 /// Default implementation delegates to [`Self::get_keyed_any`]; containers
209 /// override it to return `None`/`Err` without panicking.
210 fn try_get_keyed_any_result(
211 &self,
212 key: &str,
213 variant: &str,
214 ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
215 Ok(self.get_keyed_any(key, variant))
216 }
217
218 /// Resolve all **unkeyed** registered instances of `T`, in registration
219 /// order.
220 ///
221 /// Equivalent to C# MEDI `GetServices<T>()` (which also excludes keyed
222 /// services). Types implementing `IServiceResolver` get this for free via
223 /// `get_all_any`; types with an inherent `get_all<T>` method (e.g.
224 /// `ServiceProvider`, `Scope`) use that instead — Rust's method resolution
225 /// gives inherent methods priority over trait methods.
226 fn get_all<T: ?Sized + Sync + Send + 'static>(&self) -> Vec<Arc<T>>
227 where
228 Self: Sized,
229 {
230 self.get_all_any(std::any::type_name::<T>())
231 .into_iter()
232 .filter_map(|a| a.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d)))
233 .collect()
234 }
235
236 /// Type-erased owned resolution: bypass cache, call factory fresh, return
237 /// `Arc<dyn Any + Send + Sync>` (concrete `Arc<Arc<T>>`).
238 ///
239 /// - Singleton → `None` (shared singleton cannot be owned).
240 /// - Scoped/Transient → factory invoked directly (no cache read/write),
241 /// caller downcasts + `Arc::try_unwrap` to obtain owned `T`.
242 ///
243 /// Default `None` keeps external implementors backward-compatible.
244 fn get_owned_any(&self, _key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
245 None
246 }
247 fn get_keyed_owned_any(
248 &self,
249 _key: &str,
250 _variant: &str,
251 ) -> Option<Arc<dyn Any + Send + Sync>> {
252 None
253 }
254
255 /// Resolve owned `T` (bypass cache, fresh each call).
256 /// Returns `Err` if not registered or the service is Singleton.
257 fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError>
258 where
259 Self: Sized,
260 {
261 let type_name = std::any::type_name::<T>();
262 let arc_any = self
263 .get_owned_any(type_name)
264 .ok_or_else(|| RdiError::not_found(type_name, None))?;
265 let arc_t: Arc<T> = arc_any
266 .downcast::<Arc<T>>()
267 .ok()
268 .map(|d| Arc::clone(&*d))
269 .ok_or_else(|| RdiError::not_found(type_name, None))?;
270 Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
271 }
272
273 /// Resolve owned `T`, returning `None` if not registered or Singleton.
274 fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T>
275 where
276 Self: Sized,
277 {
278 let arc_any = self.get_owned_any(std::any::type_name::<T>())?;
279 let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
280 Arc::try_unwrap(arc_t).ok()
281 }
282
283 /// Resolve owned keyed `T`.
284 /// Returns `Err` if not found or Singleton.
285 fn get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Result<T, RdiError>
286 where
287 Self: Sized,
288 {
289 let type_name = std::any::type_name::<T>();
290 let arc_any = self
291 .get_keyed_owned_any(type_name, variant)
292 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
293 let arc_t: Arc<T> = arc_any
294 .downcast::<Arc<T>>()
295 .ok()
296 .map(|d| Arc::clone(&*d))
297 .ok_or_else(|| RdiError::keyed_not_found(type_name, variant, None))?;
298 Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
299 }
300
301 /// Resolve owned keyed `T`, returning `None` if not found or Singleton.
302 fn try_get_keyed_owned<T: Send + Sync + 'static>(&self, variant: &str) -> Option<T>
303 where
304 Self: Sized,
305 {
306 let arc_any = self.get_keyed_owned_any(std::any::type_name::<T>(), variant)?;
307 let arc_t: Arc<T> = arc_any.downcast::<Arc<T>>().ok().map(|d| Arc::clone(&*d))?;
308 Arc::try_unwrap(arc_t).ok()
309 }
310}
311
312pub struct ServiceEntry {
313 pub cache_key: usize,
314 pub key: Option<String>,
315 pub type_name: &'static str, // kept for IServiceResolver string-based resolution
316 pub factory: ServiceFactory,
317 pub async_factory: Option<AsyncServiceFactory>,
318 pub lifetime: ServiceLifetime,
319}
320
321pub type ServiceFactory =
322 Arc<dyn Fn(&dyn IServiceResolver) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
323
324/// Async variant of [`ServiceFactory`] — for services that require async
325/// initialization (e.g., database connections, remote config loading).
326///
327/// Used with [`ServiceCollection::build_async`](crate::collection::ServiceCollection::build_async).
328/// The factory receives an `Arc<ServiceProvider>` (instead of `&dyn IServiceResolver`)
329/// because async closures cannot borrow from the caller's stack frame.
330pub type AsyncServiceFactory = Arc<
331 dyn Fn(Arc<ServiceProvider>) -> Pin<Box<dyn Future<Output = Arc<dyn Any + Send + Sync>> + Send>>
332 + Send
333 + Sync,
334>;