Skip to main content

hosting_di/
service.rs

1use crate::{BoxError, Dependency, ResolveError, Resolver};
2use std::fmt;
3use std::sync::Arc;
4
5/// A type the container can construct.
6///
7/// Usually derived with `#[derive(Service)]`. Constructors must not block on
8/// other threads that resolve services from the same container: construction
9/// of cached instances is serialised per container and per scope.
10pub trait Service: Sized + Send + Sync + 'static {
11    /// The services `construct` resolves, used to validate the graph at build.
12    fn dependencies() -> Vec<Dependency>;
13
14    /// Builds the service, resolving its dependencies through `resolver`.
15    fn construct(resolver: &Resolver<'_>) -> Result<Self, ResolveError>;
16}
17
18/// A value the container can supply as a field or constructor argument.
19#[doc(hidden)]
20#[diagnostic::on_unimplemented(
21    message = "`{Self}` cannot be injected",
22    label = "not a dependency",
23    note = "fields must be `Arc<T>` or `Vec<Arc<T>>`; use `#[service(constructor = ...)]` for other fields"
24)]
25pub trait Inject: Sized {
26    fn dependency() -> Dependency;
27
28    fn inject(resolver: &Resolver<'_>) -> Result<Self, ResolveError>;
29}
30
31impl<T: ?Sized + Send + Sync + 'static> Inject for Arc<T> {
32    fn dependency() -> Dependency {
33        Dependency::on::<T>()
34    }
35
36    fn inject(resolver: &Resolver<'_>) -> Result<Self, ResolveError> {
37        resolver.resolve()
38    }
39}
40
41impl<T: ?Sized + Send + Sync + 'static> Inject for Vec<Arc<T>> {
42    fn dependency() -> Dependency {
43        Dependency::all::<T>()
44    }
45
46    fn inject(resolver: &Resolver<'_>) -> Result<Self, ResolveError> {
47        resolver.resolve_all()
48    }
49}
50
51/// What a named constructor may return: `S` or `Result<S, E>`.
52#[doc(hidden)]
53#[diagnostic::on_unimplemented(
54    message = "service constructor must return `{S}` or `Result<{S}, E>`",
55    note = "constructors are synchronous; move async setup into a hosted service"
56)]
57pub trait ConstructorOutput<S> {
58    fn into_result(self) -> Result<S, BoxError>;
59}
60
61impl<S> ConstructorOutput<S> for S {
62    fn into_result(self) -> Result<S, BoxError> {
63        Ok(self)
64    }
65}
66
67impl<S, E: Into<BoxError>> ConstructorOutput<S> for Result<S, E> {
68    fn into_result(self) -> Result<S, BoxError> {
69        self.map_err(Into::into)
70    }
71}
72
73/// A function whose arguments can all be injected, building `S`.
74#[doc(hidden)]
75#[diagnostic::on_unimplemented(
76    message = "`{Self}` cannot construct `{S}`",
77    label = "not a service constructor",
78    note = "a service constructor takes injectable parameters such as `Arc<T>` \
79            and returns `{S}` or `Result<{S}, E>`",
80    note = "constructors are synchronous; move async setup into a hosted service"
81)]
82pub trait Constructor<S, Args> {
83    fn dependencies() -> Vec<Dependency>;
84
85    fn call(&self, resolver: &Resolver<'_>) -> Result<S, ResolveError>;
86}
87
88macro_rules! constructor {
89    ($($arg:ident),*) => {
90        impl<F, R, S, $($arg: Inject),*> Constructor<S, ($($arg,)*)> for F
91        where
92            F: Fn($($arg),*) -> R,
93            R: ConstructorOutput<S>,
94        {
95            fn dependencies() -> Vec<Dependency> {
96                vec![$($arg::dependency()),*]
97            }
98
99            #[allow(non_snake_case)]
100            fn call(&self, resolver: &Resolver<'_>) -> Result<S, ResolveError> {
101                $(let $arg = $arg::inject(resolver)?;)*
102                self($($arg),*)
103                    .into_result()
104                    .map_err(|error| resolver.failure(error))
105            }
106        }
107    };
108}
109
110constructor!();
111constructor!(A1);
112constructor!(A1, A2);
113constructor!(A1, A2, A3);
114constructor!(A1, A2, A3, A4);
115constructor!(A1, A2, A3, A4, A5);
116constructor!(A1, A2, A3, A4, A5, A6);
117constructor!(A1, A2, A3, A4, A5, A6, A7);
118constructor!(A1, A2, A3, A4, A5, A6, A7, A8);
119constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
120constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
121constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
122constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
123constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
124constructor!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
125constructor!(
126    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15
127);
128constructor!(
129    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16
130);
131
132/// Checks `f` once so derive output reports a bad constructor in one error.
133#[doc(hidden)]
134pub fn constructor<S, Args, F: Constructor<S, Args>>(f: &F) -> Bound<'_, F, S> {
135    Bound {
136        f,
137        dependencies: F::dependencies,
138        call: F::call,
139    }
140}
141
142/// A constructor whose bound was checked by [`constructor`]; its methods carry
143/// no bounds so a bad constructor produces no follow-on errors.
144#[doc(hidden)]
145pub struct Bound<'a, F, S> {
146    f: &'a F,
147    dependencies: fn() -> Vec<Dependency>,
148    call: fn(&F, &Resolver<'_>) -> Result<S, ResolveError>,
149}
150
151impl<F, S> fmt::Debug for Bound<'_, F, S> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        f.debug_struct("Bound").finish_non_exhaustive()
154    }
155}
156
157impl<F, S> Bound<'_, F, S> {
158    pub fn dependencies(&self) -> Vec<Dependency> {
159        (self.dependencies)()
160    }
161
162    pub fn call(&self, resolver: &Resolver<'_>) -> Result<S, ResolveError> {
163        (self.call)(self.f, resolver)
164    }
165}