1use crate::{BoxError, Dependency, ResolveError, Resolver};
2use std::fmt;
3use std::sync::Arc;
4
5pub trait Service: Sized + Send + Sync + 'static {
11 fn dependencies() -> Vec<Dependency>;
13
14 fn construct(resolver: &Resolver<'_>) -> Result<Self, ResolveError>;
16}
17
18#[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#[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#[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#[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#[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}