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    pub fn instance<T: Send + Sync + 'static>(mut self, v: Arc<T>) -> Self {
261        let ff: ServiceFactory = Arc::new(move |_| Arc::new(v.clone()));
262        self.descriptors.push(ServiceDescriptor {
263            type_id: std::any::TypeId::of::<T>(),
264            type_name: std::any::type_name::<T>(),
265            key: None,
266            factory: ff,
267            keyed_factory: None,
268            async_factory: None,
269            lifetime: ServiceLifetime::Singleton,
270        });
271        self
272    }
273
274    pub fn singleton_value<T: Send + Sync + 'static>(self, v: T) -> Self {
275        self.instance(Arc::new(v))
276    }
277
278    pub fn build(self) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
279        self.build_with(crate::provider::ServiceProviderOptions::default())
280    }
281
282    /// Build with explicit [`ServiceProviderOptions`](crate::ServiceProviderOptions).
283    ///
284    /// `build()` equals `build_with(ServiceProviderOptions::default())`, i.e.
285    /// strict scope validation is enabled by default (MEDI `ValidateScopes`).
286    /// Use [`crate::ServiceProviderOptions::lenient`] to restore the ≤ 0.7
287    /// root-scope behavior where the root provider caches scoped services.
288    pub fn build_with(
289        self,
290        options: crate::provider::ServiceProviderOptions,
291    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
292        crate::provider::ServiceProvider::new_with(self.into_store(), options)
293    }
294
295    /// Build the provider, executing async factories for singleton services.
296    ///
297    /// Async singleton factories are run during build and their results are
298    /// cached as singleton instances. Async transient/scoped factories are
299    /// stored for later resolution.
300    ///
301    /// Use this instead of [`build`](Self::build) when any service requires
302    /// async initialization (e.g., database connections, remote config).
303    pub async fn build_async(
304        self,
305    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
306        self.build_async_with(crate::provider::ServiceProviderOptions::default())
307            .await
308    }
309
310    /// Async variant of [`build_with`](Self::build_with).
311    pub async fn build_async_with(
312        self,
313        options: crate::provider::ServiceProviderOptions,
314    ) -> Result<Arc<crate::provider::ServiceProvider>, crate::error::RdiError> {
315        crate::provider::ServiceProvider::new_async_with(self.into_store(), options).await
316    }
317
318    /// Materialize descriptors into the by-TypeId `ServiceStore`.
319    ///
320    /// `cache_key` is the descriptor index (registration order); it drives
321    /// deterministic eager initialization and reverse-order disposal.
322    fn into_store(self) -> crate::entry::ServiceStore {
323        let mut s = crate::entry::ServiceStore::new();
324        for (n, d) in self.descriptors.into_iter().enumerate() {
325            let e = crate::entry::ServiceEntry {
326                cache_key: n,
327                key: d.key,
328                type_name: d.type_name,
329                factory: d.factory,
330                keyed_factory: d.keyed_factory,
331                async_factory: d.async_factory,
332                lifetime: d.lifetime,
333            };
334            s.entry(d.type_id).or_default().push(e);
335        }
336        s
337    }
338
339    /// Build a `ServiceCollection` from all `#[rust_dix::inject]` annotations
340    /// in the current binary (across all crates).
341    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
342    pub fn from_injected() -> Self {
343        let mut descriptors = Vec::new();
344        for reg in inventory::iter::<ServiceRegistration> {
345            let factory: ServiceFactory = Arc::new(move |r| (reg.factory)(r));
346            descriptors.push(ServiceDescriptor {
347                type_id: reg.type_id,
348                type_name: (reg.type_name_fn)(),
349                key: None,
350                factory,
351                keyed_factory: None,
352                async_factory: None,
353                lifetime: reg.lifetime,
354            });
355        }
356        Self { descriptors }
357    }
358
359    /// Register a sync service (unkeyed).
360    fn push<T: ?Sized + Send + Sync + 'static>(
361        &mut self,
362        lt: ServiceLifetime,
363        key: Option<ServiceKey>,
364        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
365    ) {
366        let sf: ServiceFactory = Arc::new(move |r| {
367            let val: Arc<T> = (f)(r);
368            Arc::new(val) as Arc<dyn Any + Send + Sync>
369        });
370        self.descriptors.push(ServiceDescriptor {
371            type_id: std::any::TypeId::of::<T>(),
372            type_name: std::any::type_name::<T>(),
373            key,
374            factory: sf,
375            keyed_factory: None,
376            async_factory: None,
377            lifetime: lt,
378        });
379    }
380
381    /// Register a sync keyed service.
382    fn push_keyed<T: ?Sized + Send + Sync + 'static>(
383        &mut self,
384        lt: ServiceLifetime,
385        key: ServiceKey,
386        f: impl Fn(&dyn crate::entry::IServiceResolver) -> Arc<T> + Send + Sync + 'static,
387    ) {
388        self.push(lt, Some(key), f);
389    }
390
391    /// Register an `AnyKey` fallback sync service (key-aware factory).
392    fn push_any_keyed<T: ?Sized + Send + Sync + 'static>(
393        &mut self,
394        lt: ServiceLifetime,
395        f: impl Fn(&dyn crate::entry::IServiceResolver, &ServiceKey) -> Arc<T> + Send + Sync + 'static,
396    ) {
397        let keyed: KeyedServiceFactory = Arc::new(move |r, key| {
398            let val: Arc<T> = (f)(r, key);
399            Arc::new(val) as Arc<dyn Any + Send + Sync>
400        });
401        // The plain factory must never run for an `AnyKey` registration; it only
402        // exists because every `ServiceEntry` carries one. Running it would be a
403        // framework bug, so fail loudly with a typed error.
404        let stub: ServiceFactory = Arc::new(|_| {
405            std::panic::panic_any(crate::error::RdiError::FactoryPanic {
406                service: "AnyKey registration".into(),
407                payload: "AnyKey registrations require a requested key and cannot be \
408                          resolved without one"
409                    .into(),
410            })
411        });
412        self.descriptors.push(ServiceDescriptor {
413            type_id: std::any::TypeId::of::<T>(),
414            type_name: std::any::type_name::<T>(),
415            key: Some(ServiceKey::any_key()),
416            factory: stub,
417            keyed_factory: Some(keyed),
418            async_factory: None,
419            lifetime: lt,
420        });
421    }
422
423    /// Register an async service (legacy `Arc<ServiceProvider>` factory).
424    fn push_async<T: ?Sized + Send + Sync + 'static>(
425        &mut self,
426        lt: ServiceLifetime,
427        key: Option<ServiceKey>,
428        f: impl Fn(
429                Arc<crate::provider::ServiceProvider>,
430            ) -> Pin<Box<dyn Future<Output = Arc<T>> + Send>>
431            + Send
432            + Sync
433            + 'static,
434    ) {
435        let af: AsyncServiceFactory = Arc::new(move |sp: Arc<crate::provider::ServiceProvider>| {
436            let fut = (f)(sp);
437            Box::pin(async move {
438                let val: Arc<T> = fut.await;
439                Arc::new(val) as Arc<dyn Any + Send + Sync>
440            }) as Pin<Box<dyn Future<Output = Arc<dyn Any + Send + Sync>> + Send>>
441        });
442        // Sync factory: typed error if invoked before async initialization.
443        let type_name = std::any::type_name::<T>();
444        let sf: ServiceFactory = Arc::new(move |_| {
445            std::panic::panic_any(crate::error::RdiError::AsyncServiceRequiresGetAsync {
446                service: type_name.to_string(),
447            })
448        });
449        self.descriptors.push(ServiceDescriptor {
450            type_id: std::any::TypeId::of::<T>(),
451            type_name,
452            key,
453            factory: sf,
454            keyed_factory: None,
455            async_factory: Some(af),
456            lifetime: lt,
457        });
458    }
459}
460
461impl Default for ServiceCollection {
462    fn default() -> Self {
463        Self::new()
464    }
465}