Skip to main content

hosting_di/
collection.rs

1use crate::container::{ConstructFn, Container, Erased};
2use crate::graph::Graph;
3use crate::key::Key;
4use crate::{
5    BoxError, BuildError, BuildIssue, CastFrom, Dependency, Lifetime, Resolver, Service,
6    ServiceProvider,
7};
8use std::any::{Any, type_name};
9use std::fmt;
10use std::sync::Arc;
11
12/// Services registered for an application, frozen into a [`ServiceProvider`]
13/// by [`build`](Self::build).
14///
15/// Registering a type again adds another registration: a single resolve uses
16/// the last one, and `resolve_all` returns every one in order. Singletons are
17/// constructed outside any scope, so a singleton that depends on a scoped
18/// service fails to resolve rather than holding on to one scope's instance.
19/// Transients are constructed in the scope that requested them.
20///
21/// # Validation
22///
23/// [`build`](Self::build) always rejects dependency cycles. Two more checks
24/// are opt-in, and every problem found is reported in one [`BuildError`]:
25///
26/// ```
27/// # use hosting_di::{Dependency, ResolveError, Resolver, Service};
28/// use hosting_di::{BuildIssue, ServiceCollection};
29/// # struct Pool;
30/// # struct Database;
31/// # impl Service for Database {
32/// #     fn dependencies() -> Vec<Dependency> {
33/// #         vec![Dependency::on::<Pool>()]
34/// #     }
35/// #     fn construct(_: &Resolver<'_>) -> Result<Self, ResolveError> {
36/// #         Ok(Database)
37/// #     }
38/// # }
39/// let mut services = ServiceCollection::new();
40/// services
41///     .validate_dependencies()
42///     .validate_scopes()
43///     .add_singleton::<Database>(); // needs a Pool, which isn't registered
44///
45/// let err = services.build().unwrap_err();
46/// assert!(matches!(err.issues(), [BuildIssue::MissingDependency { .. }]));
47/// ```
48#[derive(Debug, Default)]
49pub struct ServiceCollection {
50    registrations: Vec<Registration>,
51    validation: Validation,
52}
53
54/// Opt-in checks run by `build`.
55#[derive(Debug, Default)]
56struct Validation {
57    dependencies: bool,
58    scopes: bool,
59}
60
61pub(crate) struct Registration {
62    pub(crate) key: Key,
63    /// Type name of what is constructed; differs from the key's name when a
64    /// service is registered as a trait.
65    pub(crate) implementation: &'static str,
66    pub(crate) lifetime: Lifetime,
67    pub(crate) source: Source,
68    /// Declared dependencies; `None` for factories and instances.
69    pub(crate) dependencies: Option<fn() -> Vec<Dependency>>,
70    /// A `fn(Erased) -> Option<Arc<I>>` for the registered service type `I`.
71    pub(crate) cast: Box<dyn Any + Send + Sync>,
72    /// Index into the storage of this registration's lifetime, set by the container.
73    pub(crate) slot: usize,
74}
75
76impl fmt::Debug for Registration {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        f.debug_struct("Registration")
79            .field("service", &self.key.name)
80            .field("implementation", &self.implementation)
81            .field("lifetime", &self.lifetime)
82            .finish_non_exhaustive()
83    }
84}
85
86pub(crate) enum Source {
87    Construct(ConstructFn),
88    Instance(Erased),
89}
90
91impl ServiceCollection {
92    /// Creates an empty collection.
93    pub fn new() -> Self {
94        Self::default()
95    }
96
97    /// Registers `T` with one instance shared for the life of the provider.
98    pub fn add_singleton<T: Service>(&mut self) -> &mut Self {
99        self.add::<T>(Lifetime::Singleton)
100    }
101
102    /// Registers `T` with one instance per scope.
103    pub fn add_scoped<T: Service>(&mut self) -> &mut Self {
104        self.add::<T>(Lifetime::Scoped)
105    }
106
107    /// Registers `T` with a new instance on every resolution.
108    pub fn add_transient<T: Service>(&mut self) -> &mut Self {
109        self.add::<T>(Lifetime::Transient)
110    }
111
112    /// Registers `T` as an implementation of the trait object `I`, with one
113    /// instance shared for the life of the provider.
114    ///
115    /// `T` is reachable only as `I`; register it separately to resolve it by
116    /// its own type.
117    pub fn add_singleton_as<I, T>(&mut self) -> &mut Self
118    where
119        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
120        T: Service,
121    {
122        self.add_service::<I, T>(Lifetime::Singleton, trait_cast::<I, T>())
123    }
124
125    /// Registers `T` as an implementation of `I`, with one instance per scope.
126    pub fn add_scoped_as<I, T>(&mut self) -> &mut Self
127    where
128        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
129        T: Service,
130    {
131        self.add_service::<I, T>(Lifetime::Scoped, trait_cast::<I, T>())
132    }
133
134    /// Registers `T` as an implementation of `I`, constructed on every
135    /// resolution.
136    pub fn add_transient_as<I, T>(&mut self) -> &mut Self
137    where
138        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
139        T: Service,
140    {
141        self.add_service::<I, T>(Lifetime::Transient, trait_cast::<I, T>())
142    }
143
144    /// Whether anything is registered as `T`.
145    ///
146    /// Covers every form of registration, so it can guard any of them, for
147    /// example to add a default only when the application hasn't.
148    pub fn contains<T: ?Sized + 'static>(&self) -> bool {
149        let key = Key::of::<T>();
150        self.registrations
151            .iter()
152            .any(|registration| registration.key == key)
153    }
154
155    /// Like [`add_singleton`](Self::add_singleton), but only if nothing is
156    /// registered as `T` yet.
157    pub fn try_add_singleton<T: Service>(&mut self) -> &mut Self {
158        if self.contains::<T>() {
159            return self;
160        }
161        self.add_singleton::<T>()
162    }
163
164    /// Like [`add_scoped`](Self::add_scoped), but only if nothing is
165    /// registered as `T` yet.
166    pub fn try_add_scoped<T: Service>(&mut self) -> &mut Self {
167        if self.contains::<T>() {
168            return self;
169        }
170        self.add_scoped::<T>()
171    }
172
173    /// Like [`add_transient`](Self::add_transient), but only if nothing is
174    /// registered as `T` yet.
175    pub fn try_add_transient<T: Service>(&mut self) -> &mut Self {
176        if self.contains::<T>() {
177            return self;
178        }
179        self.add_transient::<T>()
180    }
181
182    /// Like [`add_singleton_as`](Self::add_singleton_as), but only if nothing
183    /// is registered as `I` yet, whatever its implementation.
184    pub fn try_add_singleton_as<I, T>(&mut self) -> &mut Self
185    where
186        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
187        T: Service,
188    {
189        if self.contains::<I>() {
190            return self;
191        }
192        self.add_singleton_as::<I, T>()
193    }
194
195    /// Like [`add_scoped_as`](Self::add_scoped_as), but only if nothing is
196    /// registered as `I` yet, whatever its implementation.
197    pub fn try_add_scoped_as<I, T>(&mut self) -> &mut Self
198    where
199        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
200        T: Service,
201    {
202        if self.contains::<I>() {
203            return self;
204        }
205        self.add_scoped_as::<I, T>()
206    }
207
208    /// Like [`add_transient_as`](Self::add_transient_as), but only if nothing
209    /// is registered as `I` yet, whatever its implementation.
210    pub fn try_add_transient_as<I, T>(&mut self) -> &mut Self
211    where
212        I: ?Sized + CastFrom<T> + Send + Sync + 'static,
213        T: Service,
214    {
215        if self.contains::<I>() {
216            return self;
217        }
218        self.add_transient_as::<I, T>()
219    }
220
221    /// Registers a factory for `T` with one instance shared for the life of the
222    /// provider.
223    ///
224    /// Errors from resolving dependencies inside the factory keep their original
225    /// path; any other error is reported as a construction failure of `T`.
226    /// Like constructors, factories must not block on other threads that
227    /// resolve from the same container.
228    pub fn add_singleton_with<T, F>(&mut self, factory: F) -> &mut Self
229    where
230        T: Send + Sync + 'static,
231        F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
232    {
233        self.add_factory(Lifetime::Singleton, factory)
234    }
235
236    /// Registers a factory for `T` with one instance per scope.
237    pub fn add_scoped_with<T, F>(&mut self, factory: F) -> &mut Self
238    where
239        T: Send + Sync + 'static,
240        F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
241    {
242        self.add_factory(Lifetime::Scoped, factory)
243    }
244
245    /// Registers a factory for `T` that runs on every resolution.
246    pub fn add_transient_with<T, F>(&mut self, factory: F) -> &mut Self
247    where
248        T: Send + Sync + 'static,
249        F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
250    {
251        self.add_factory(Lifetime::Transient, factory)
252    }
253
254    /// Registers an already constructed singleton.
255    pub fn add_singleton_instance<T: Send + Sync + 'static>(&mut self, instance: T) -> &mut Self {
256        self.push_unchecked::<T>(Lifetime::Singleton, Source::Instance(Arc::new(instance)))
257    }
258
259    /// Registers a factory producing the trait object `I`, with one instance
260    /// shared for the life of the provider.
261    ///
262    /// Unlike [`add_singleton_as`](Self::add_singleton_as), the trait needs no
263    /// `#[abstraction]`, so traits from other crates can be registered. The
264    /// concrete type behind the returned `Arc` is not known, so errors and
265    /// logs name these registrations by `I`.
266    pub fn add_singleton_as_with<I, F>(&mut self, factory: F) -> &mut Self
267    where
268        I: ?Sized + Send + Sync + 'static,
269        F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
270    {
271        self.add_factory_as(Lifetime::Singleton, factory)
272    }
273
274    /// Registers a factory producing the trait object `I`, with one instance
275    /// per scope.
276    pub fn add_scoped_as_with<I, F>(&mut self, factory: F) -> &mut Self
277    where
278        I: ?Sized + Send + Sync + 'static,
279        F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
280    {
281        self.add_factory_as(Lifetime::Scoped, factory)
282    }
283
284    /// Registers a factory producing the trait object `I`, run on every
285    /// resolution.
286    ///
287    /// The returned `Arc` is stored behind one more allocation, so each
288    /// resolution allocates once beyond what the factory does.
289    pub fn add_transient_as_with<I, F>(&mut self, factory: F) -> &mut Self
290    where
291        I: ?Sized + Send + Sync + 'static,
292        F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
293    {
294        self.add_factory_as(Lifetime::Transient, factory)
295    }
296
297    /// Registers an already constructed trait object as a singleton.
298    pub fn add_singleton_instance_as<I>(&mut self, instance: Arc<I>) -> &mut Self
299    where
300        I: ?Sized + Send + Sync + 'static,
301    {
302        self.push_trait_object::<I>(Lifetime::Singleton, Source::Instance(Arc::new(instance)))
303    }
304
305    /// Rejects at build any declared dependency that has no registration.
306    ///
307    /// Without it, a missing dependency surfaces when the service is first
308    /// resolved. A `Vec<Arc<T>>` dependency is never missing, since it may be
309    /// empty, and factories are not checked because their dependencies are
310    /// not declared.
311    pub fn validate_dependencies(&mut self) -> &mut Self {
312        self.validation.dependencies = true;
313        self
314    }
315
316    /// Rejects at build any singleton that depends on a scoped service,
317    /// directly or through transients.
318    ///
319    /// Without it, such a singleton fails to resolve with
320    /// [`ResolveError::ScopedFromRoot`](crate::ResolveError::ScopedFromRoot);
321    /// it never silently keeps one scope's instance.
322    pub fn validate_scopes(&mut self) -> &mut Self {
323        self.validation.scopes = true;
324        self
325    }
326
327    /// Freezes the registrations into a provider.
328    ///
329    /// Fails if registered services depend on each other in a cycle, or if an
330    /// enabled check finds a problem; every problem found is reported.
331    pub fn build(self) -> Result<ServiceProvider, BuildError> {
332        let graph = Graph::new(&self.registrations);
333        let mut issues: Vec<BuildIssue> = graph
334            .cycles()
335            .into_iter()
336            .map(|path| BuildIssue::Cycle { path })
337            .collect();
338        if self.validation.dependencies {
339            issues.extend(
340                graph
341                    .missing()
342                    .into_iter()
343                    .map(|path| BuildIssue::MissingDependency { path }),
344            );
345        }
346        if self.validation.scopes {
347            issues.extend(
348                graph
349                    .captive()
350                    .into_iter()
351                    .map(|path| BuildIssue::CaptiveDependency { path }),
352            );
353        }
354        if !issues.is_empty() {
355            return Err(BuildError::new(issues));
356        }
357        Ok(ServiceProvider::new(Container::new(self.registrations)))
358    }
359
360    fn add<T: Service>(&mut self, lifetime: Lifetime) -> &mut Self {
361        self.add_service::<T, T>(lifetime, concrete_cast::<T>())
362    }
363
364    fn add_service<I: ?Sized + 'static, T: Service>(
365        &mut self,
366        lifetime: Lifetime,
367        cast: Cast,
368    ) -> &mut Self {
369        let construct: ConstructFn =
370            Arc::new(|resolver| T::construct(resolver).map(|service| Arc::new(service) as Erased));
371        self.push(Registration {
372            key: Key::of::<I>(),
373            implementation: type_name::<T>(),
374            lifetime,
375            source: Source::Construct(construct),
376            dependencies: Some(T::dependencies),
377            cast,
378            slot: 0,
379        })
380    }
381
382    fn add_factory<T, F>(&mut self, lifetime: Lifetime, factory: F) -> &mut Self
383    where
384        T: Send + Sync + 'static,
385        F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
386    {
387        self.push_unchecked::<T>(lifetime, Source::Construct(factory_construct(factory)))
388    }
389
390    /// Registers a factory or instance whose dependencies are not declared.
391    fn push_unchecked<T: Send + Sync + 'static>(
392        &mut self,
393        lifetime: Lifetime,
394        source: Source,
395    ) -> &mut Self {
396        let key = Key::of::<T>();
397        self.push(Registration {
398            key,
399            implementation: key.name,
400            lifetime,
401            source,
402            dependencies: None,
403            cast: concrete_cast::<T>(),
404            slot: 0,
405        })
406    }
407
408    fn add_factory_as<I, F>(&mut self, lifetime: Lifetime, factory: F) -> &mut Self
409    where
410        I: ?Sized + Send + Sync + 'static,
411        F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
412    {
413        self.push_trait_object::<I>(lifetime, Source::Construct(factory_construct(factory)))
414    }
415
416    /// Registers an `Arc<I>` stored as is, since an unsized `I` cannot be
417    /// erased directly.
418    fn push_trait_object<I>(&mut self, lifetime: Lifetime, source: Source) -> &mut Self
419    where
420        I: ?Sized + Send + Sync + 'static,
421    {
422        let key = Key::of::<I>();
423        let cast: fn(Erased) -> Option<Arc<I>> = |instance| {
424            instance
425                .downcast::<Arc<I>>()
426                .ok()
427                .map(|arc| Arc::clone(arc.as_ref()))
428        };
429        self.push(Registration {
430            key,
431            implementation: key.name,
432            lifetime,
433            source,
434            dependencies: None,
435            cast: Box::new(cast),
436            slot: 0,
437        })
438    }
439
440    fn push(&mut self, registration: Registration) -> &mut Self {
441        self.registrations.push(registration);
442        self
443    }
444}
445
446/// Wraps a factory as a constructor, passing a dependency's resolution error
447/// through unchanged.
448fn factory_construct<T, F>(factory: F) -> ConstructFn
449where
450    T: Send + Sync + 'static,
451    F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
452{
453    Arc::new(move |resolver| match factory(resolver) {
454        Ok(service) => Ok(Arc::new(service) as Erased),
455        Err(error) => Err(resolver.failure(error)),
456    })
457}
458
459/// A boxed `fn(Erased) -> Option<Arc<I>>` for the registered service type `I`.
460type Cast = Box<dyn Any + Send + Sync>;
461
462fn trait_cast<I, T>() -> Cast
463where
464    I: ?Sized + CastFrom<T> + 'static,
465    T: Send + Sync + 'static,
466{
467    let cast: fn(Erased) -> Option<Arc<I>> = |instance| instance.downcast::<T>().ok().map(I::cast);
468    Box::new(cast)
469}
470
471fn concrete_cast<T: Send + Sync + 'static>() -> Cast {
472    let cast: fn(Erased) -> Option<Arc<T>> = |instance| instance.downcast().ok();
473    Box::new(cast)
474}