Skip to main content

rust_dix/
collection.rs

1use crate::entry::{
2    AsyncServiceFactory, KeyedServiceFactory, ServiceDescriptor, ServiceFactory, ServiceLifetime,
3};
4use crate::key::ServiceKey;
5use crate::registration::ServiceRegistration;
6use std::any::Any;
7use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10
11pub struct ServiceCollection {
12    descriptors: Vec<ServiceDescriptor>,
13}
14
15impl ServiceCollection {
16    pub fn new() -> Self {
17        Self {
18            descriptors: Vec::new(),
19        }
20    }
21
22    pub fn singleton<T: ?Sized + Send + Sync + 'static>(
23        mut self,
24        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
25    ) -> Self {
26        self.push(ServiceLifetime::Singleton, None, f);
27        self
28    }
29
30    pub fn transient<T: ?Sized + Send + Sync + 'static>(
31        mut self,
32        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
33    ) -> Self {
34        self.push(ServiceLifetime::Transient, None, f);
35        self
36    }
37
38    pub fn scoped<T: ?Sized + Send + Sync + 'static>(
39        mut self,
40        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
41    ) -> Self {
42        self.push(ServiceLifetime::Scoped, None, f);
43        self
44    }
45
46    /// Register a keyed singleton service.
47    ///
48    /// String keys (`keyed_singleton("name", …)`) and typed keys
49    /// (`keyed_singleton(ServiceKey::new(MyKey::A), …)`) are both accepted.
50    pub fn keyed_singleton<T: ?Sized + Send + Sync + 'static>(
51        mut self,
52        k: impl Into<ServiceKey>,
53        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
54    ) -> Self {
55        self.push_keyed(ServiceLifetime::Singleton, k.into(), f);
56        self
57    }
58
59    /// Register a keyed transient service (new instance each resolution).
60    pub fn keyed_transient<T: ?Sized + Send + Sync + 'static>(
61        mut self,
62        k: impl Into<ServiceKey>,
63        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
64    ) -> Self {
65        self.push_keyed(ServiceLifetime::Transient, k.into(), f);
66        self
67    }
68
69    /// Register a keyed scoped service (shared within a scope).
70    pub fn keyed_scoped<T: ?Sized + Send + Sync + 'static>(
71        mut self,
72        k: impl Into<ServiceKey>,
73        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
74    ) -> Self {
75        self.push_keyed(ServiceLifetime::Scoped, k.into(), f);
76        self
77    }
78
79    /// Register an `AnyKey` fallback singleton (MEDI `KeyedService.AnyKey`).
80    ///
81    /// The factory receives the **requested** key and one instance is created
82    /// and cached **per requested key**. Exact-key registrations take precedence.
83    pub fn keyed_any_singleton<T: ?Sized + Send + Sync + 'static>(
84        mut self,
85        f: impl Fn(&dyn crate::entry::IServiceResolver, &ServiceKey) -> Arc<T> + Send + Sync + 'static,
86    ) -> Self {
87        self.push_any_keyed(ServiceLifetime::Singleton, f);
88        self
89    }
90
91    /// Register an `AnyKey` fallback scoped service (one instance per requested
92    /// key per scope).
93    pub fn keyed_any_scoped<T: ?Sized + Send + Sync + 'static>(
94        mut self,
95        f: impl Fn(&dyn crate::entry::IServiceResolver, &ServiceKey) -> Arc<T> + Send + Sync + 'static,
96    ) -> Self {
97        self.push_any_keyed(ServiceLifetime::Scoped, f);
98        self
99    }
100
101    /// Register an `AnyKey` fallback transient service (fresh instance per
102    /// resolution, receives the requested key).
103    pub fn keyed_any_transient<T: ?Sized + Send + Sync + 'static>(
104        mut self,
105        f: impl Fn(&dyn crate::entry::IServiceResolver, &ServiceKey) -> Arc<T> + Send + Sync + 'static,
106    ) -> Self {
107        self.push_any_keyed(ServiceLifetime::Transient, f);
108        self
109    }
110
111    /// Register an async singleton service (initialized once during `build_async`).
112    ///
113    /// The factory receives `Arc<ServiceProvider>`. To resolve services from
114    /// within the factory, use `get_async`; to propagate the resolution context
115    /// to spawned tasks (enabling cross-task cycle detection), call
116    /// [`ServiceProvider::async_context`](crate::ServiceProvider::async_context).
117    pub fn async_singleton<T: ?Sized + Send + Sync + 'static>(
118        mut self,
119        f: impl Fn(
120                Arc<crate::provider::ServiceProvider>,
121            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
122            + Send
123            + Sync
124            + 'static,
125    ) -> Self {
126        self.push_async(ServiceLifetime::Singleton, None, f);
127        self
128    }
129
130    /// Register an async transient service (new async instance each resolution).
131    pub fn async_transient<T: ?Sized + Send + Sync + 'static>(
132        mut self,
133        f: impl Fn(
134                Arc<crate::provider::ServiceProvider>,
135            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
136            + Send
137            + Sync
138            + 'static,
139    ) -> Self {
140        self.push_async(ServiceLifetime::Transient, None, f);
141        self
142    }
143
144    /// Register an async scoped service (shared within a scope).
145    pub fn async_scoped<T: ?Sized + Send + Sync + 'static>(
146        mut self,
147        f: impl Fn(
148                Arc<crate::provider::ServiceProvider>,
149            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
150            + Send
151            + Sync
152            + 'static,
153    ) -> Self {
154        self.push_async(ServiceLifetime::Scoped, None, f);
155        self
156    }
157
158    /// Register an async keyed singleton service.
159    pub fn async_keyed_singleton<T: ?Sized + Send + Sync + 'static>(
160        mut self,
161        k: impl Into<ServiceKey>,
162        f: impl Fn(
163                Arc<crate::provider::ServiceProvider>,
164            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
165            + Send
166            + Sync
167            + 'static,
168    ) -> Self {
169        self.push_async(ServiceLifetime::Singleton, Some(k.into()), f);
170        self
171    }
172
173    /// Register an async keyed transient service.
174    pub fn async_keyed_transient<T: ?Sized + Send + Sync + 'static>(
175        mut self,
176        k: impl Into<ServiceKey>,
177        f: impl Fn(
178                Arc<crate::provider::ServiceProvider>,
179            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
180            + Send
181            + Sync
182            + 'static,
183    ) -> Self {
184        self.push_async(ServiceLifetime::Transient, Some(k.into()), f);
185        self
186    }
187
188    /// Register an async keyed scoped service.
189    pub fn async_keyed_scoped<T: ?Sized + Send + Sync + 'static>(
190        mut self,
191        k: impl Into<ServiceKey>,
192        f: impl Fn(
193                Arc<crate::provider::ServiceProvider>,
194            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
195            + Send
196            + Sync
197            + 'static,
198    ) -> Self {
199        self.push_async(ServiceLifetime::Scoped, Some(k.into()), f);
200        self
201    }
202
203    pub fn try_add<T: ?Sized + Send + Sync + 'static>(
204        self,
205        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
206    ) -> Self {
207        self.try_add_with_lifetime(ServiceLifetime::Singleton, f)
208    }
209
210    /// Register a singleton only if no unkeyed registration for `T` exists yet.
211    pub fn try_add_singleton<T: ?Sized + Send + Sync + 'static>(
212        self,
213        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
214    ) -> Self {
215        self.try_add_with_lifetime(ServiceLifetime::Singleton, f)
216    }
217
218    /// Register a scoped service only if no unkeyed registration for `T` exists yet.
219    pub fn try_add_scoped<T: ?Sized + Send + Sync + 'static>(
220        self,
221        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
222    ) -> Self {
223        self.try_add_with_lifetime(ServiceLifetime::Scoped, f)
224    }
225
226    /// Register a transient service only if no unkeyed registration for `T` exists yet.
227    pub fn try_add_transient<T: ?Sized + Send + Sync + 'static>(
228        self,
229        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
230    ) -> Self {
231        self.try_add_with_lifetime(ServiceLifetime::Transient, f)
232    }
233
234    fn try_add_with_lifetime<T: ?Sized + Send + Sync + 'static>(
235        mut self,
236        lt: ServiceLifetime,
237        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
238    ) -> Self {
239        let tid = std::any::TypeId::of::<T>();
240        if self
241            .descriptors
242            .iter()
243            .any(|d| d.type_id == tid && d.key.is_none())
244        {
245            return self;
246        }
247        self.push(lt, None, f);
248        self
249    }
250
251    pub fn add<T: ?Sized + Send + Sync + 'static>(
252        mut self,
253        lt: ServiceLifetime,
254        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
255    ) -> Self {
256        self.push(lt, None, f);
257        self
258    }
259
260    /// Register a singleton instance of an already-created service.
261    ///
262    /// `T` may be unsized, so an existing trait-object `Arc<dyn Trait>` can be
263    /// registered directly (`instance::<dyn IClock>(Arc::new(FakeClock))`) and
264    /// resolved with `get::<dyn IClock>()`.
265    pub fn instance<T: ?Sized + Send + Sync + 'static>(mut self, v: Arc<T>) -> Self {
266        let ff: ServiceFactory = Arc::new(move |_| Arc::new(v.clone()));
267        self.descriptors.push(ServiceDescriptor {
268            type_id: std::any::TypeId::of::<T>(),
269            type_name: std::any::type_name::<T>(),
270            key: None,
271            factory: ff,
272            keyed_factory: None,
273            async_factory: None,
274            lifetime: ServiceLifetime::Singleton,
275        });
276        self
277    }
278
279    pub fn singleton_value<T: Send + Sync + 'static>(self, v: T) -> Self {
280        self.instance(Arc::new(v))
281    }
282
283    pub fn build(self) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
284        self.build_with(crate::provider::ServiceProviderOptions::default())
285    }
286
287    /// Build with explicit [`ServiceProviderOptions`](crate::ServiceProviderOptions).
288    ///
289    /// `build()` equals `build_with(ServiceProviderOptions::default())`, i.e.
290    /// strict scope validation is enabled by default (MEDI `ValidateScopes`).
291    /// Use [`crate::ServiceProviderOptions::lenient`] to restore the ≤ 0.7
292    /// root-scope behavior where the root provider caches scoped services.
293    pub fn build_with(
294        self,
295        options: crate::provider::ServiceProviderOptions,
296    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
297        crate::provider::ServiceProvider::new_with(self.into_store(), options)
298    }
299
300    /// Build the provider, executing async factories for singleton services.
301    ///
302    /// Async singleton factories are run during build and their results are
303    /// cached as singleton instances. Async transient/scoped factories are
304    /// stored for later resolution.
305    ///
306    /// Use this instead of [`build`](Self::build) when any service requires
307    /// async initialization (e.g., database connections, remote config).
308    pub async fn build_async(
309        self,
310    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
311        self.build_async_with(crate::provider::ServiceProviderOptions::default())
312            .await
313    }
314
315    /// Async variant of [`build_with`](Self::build_with).
316    pub async fn build_async_with(
317        self,
318        options: crate::provider::ServiceProviderOptions,
319    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
320        crate::provider::ServiceProvider::new_async_with(self.into_store(), options).await
321    }
322
323    /// Materialize descriptors into the by-TypeId `ServiceStore`.
324    ///
325    /// `cache_key` is the descriptor index (registration order); it drives
326    /// deterministic eager initialization and reverse-order disposal.
327    fn into_store(self) -> crate::entry::ServiceStore {
328        let mut s = crate::entry::ServiceStore::new();
329        for (n, d) in self.descriptors.into_iter().enumerate() {
330            let e = crate::entry::ServiceEntry {
331                cache_key: n,
332                key: d.key,
333                type_name: d.type_name,
334                factory: d.factory,
335                keyed_factory: d.keyed_factory,
336                async_factory: d.async_factory,
337                lifetime: d.lifetime,
338            };
339            s.entry(d.type_id).or_default().push(e);
340        }
341        s
342    }
343
344    /// Build a `ServiceCollection` from all `#[rust_dix::inject]` annotations
345    /// in the current binary (across all crates).
346    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
347    pub fn from_injected() -> Self {
348        let mut descriptors = Vec::new();
349        for reg in inventory::iter::<ServiceRegistration> {
350            let factory: ServiceFactory = Arc::new(move |r| (reg.factory)(r));
351            descriptors.push(ServiceDescriptor {
352                type_id: reg.type_id,
353                type_name: (reg.type_name_fn)(),
354                key: None,
355                factory,
356                keyed_factory: None,
357                async_factory: None,
358                lifetime: reg.lifetime,
359            });
360        }
361        Self { descriptors }
362    }
363
364    /// Register a sync service (unkeyed).
365    fn push<T: ?Sized + Send + Sync + 'static>(
366        &mut self,
367        lt: ServiceLifetime,
368        key: Option<ServiceKey>,
369        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
370    ) {
371        let sf: ServiceFactory = Arc::new(move |r| {
372            let val: Arc<T> = (f)(r);
373            Arc::new(val) as Arc<dyn Any + Send + Sync>
374        });
375        self.descriptors.push(ServiceDescriptor {
376            type_id: std::any::TypeId::of::<T>(),
377            type_name: std::any::type_name::<T>(),
378            key,
379            factory: sf,
380            keyed_factory: None,
381            async_factory: None,
382            lifetime: lt,
383        });
384    }
385
386    /// Register a sync keyed service.
387    fn push_keyed<T: ?Sized + Send + Sync + 'static>(
388        &mut self,
389        lt: ServiceLifetime,
390        key: ServiceKey,
391        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
392    ) {
393        self.push(lt, Some(key), f);
394    }
395
396    /// Register an `AnyKey` fallback sync service (key-aware factory).
397    fn push_any_keyed<T: ?Sized + Send + Sync + 'static>(
398        &mut self,
399        lt: ServiceLifetime,
400        f: impl Fn(&dyn crate::entry::IServiceResolver, &ServiceKey) -> Arc<T> + Send + Sync + 'static,
401    ) {
402        let keyed: KeyedServiceFactory = Arc::new(move |r, key| {
403            let val: Arc<T> = (f)(r, key);
404            Arc::new(val) as Arc<dyn Any + Send + Sync>
405        });
406        // The plain factory must never run for an `AnyKey` registration; it only
407        // exists because every `ServiceEntry` carries one. Running it would be a
408        // framework bug, so fail loudly with a typed error.
409        let stub: ServiceFactory = Arc::new(|_| {
410            std::panic::panic_any(crate::error::RdiError::FactoryPanic {
411                service: "AnyKey registration".into(),
412                payload: "AnyKey registrations require a requested key and cannot be \
413                          resolved without one"
414                    .into(),
415            })
416        });
417        self.descriptors.push(ServiceDescriptor {
418            type_id: std::any::TypeId::of::<T>(),
419            type_name: std::any::type_name::<T>(),
420            key: Some(ServiceKey::any_key()),
421            factory: stub,
422            keyed_factory: Some(keyed),
423            async_factory: None,
424            lifetime: lt,
425        });
426    }
427
428    /// Register an async service (legacy `Arc<ServiceProvider>` factory).
429    fn push_async<T: ?Sized + Send + Sync + 'static>(
430        &mut self,
431        lt: ServiceLifetime,
432        key: Option<ServiceKey>,
433        f: impl Fn(
434                Arc<crate::provider::ServiceProvider>,
435            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
436            + Send
437            + Sync
438            + 'static,
439    ) {
440        let af: AsyncServiceFactory = Arc::new(move |sp: Arc<crate::provider::ServiceProvider>| {
441            let fut = (f)(sp);
442            Box::pin(async move {
443                let val: Arc<T> = fut.await;
444                Arc::new(val) as Arc<dyn Any + Send + Sync>
445            }) as Pin<Box<dyn Future<Output = Arc<dyn Any + Send + Sync>> + Send>>
446        });
447        // Sync factory: typed error if invoked before async initialization.
448        let type_name = std::any::type_name::<T>();
449        let sf: ServiceFactory = Arc::new(move |_| {
450            std::panic::panic_any(crate::error::RdiError::AsyncServiceRequiresGetAsync {
451                service: type_name.to_string(),
452            })
453        });
454        self.descriptors.push(ServiceDescriptor {
455            type_id: std::any::TypeId::of::<T>(),
456            type_name,
457            key,
458            factory: sf,
459            keyed_factory: None,
460            async_factory: Some(af),
461            lifetime: lt,
462        });
463    }
464}
465
466impl Default for ServiceCollection {
467    fn default() -> Self {
468        Self::new()
469    }
470}