app_frame/dependency_injection.rs
1pub trait Provides<T> {
2 fn provide(&self) -> T;
3}
4
5impl<T: Clone> Provides<T> for T {
6 fn provide(&self) -> T {
7 self.clone()
8 }
9}
10
11pub trait ProvideA {
12 fn provide_a<T>(&self) -> T
13 where
14 Self: Provides<T>,
15 {
16 self.provide()
17 }
18}
19impl<T> ProvideA for T {}
20
21/// Syntactic sugar to make a type's dependencies injectable.
22///
23/// ```rust ignore
24/// inject!(
25/// pub struct MyStruct {
26/// some_component: SomeComponent,
27/// some_behavior: Arc<dyn SomeBehavior>,
28/// }
29/// );
30/// ```
31///
32/// Under the hood, this macro implements From<&T> where T: Provides<FieldType>
33/// for every field in the struct. If you'd like it to be constructed in a
34/// different way, you can manually implement the trait instead of using the
35/// macro.
36#[macro_export]
37macro_rules! inject {
38 (
39 $(#[$outer:meta])*
40 pub struct $Name:ident {
41 $($viz:vis $field:ident: $FieldType:ty),*$(,)?
42 }
43 ) => {
44 $(#[$outer])*
45 pub struct $Name {
46 $($viz $field: $FieldType),*
47 }
48 impl<T> From<&T> for $Name where
49 $(T: $crate::dependency_injection::Provides<$FieldType>),*
50 {
51 fn from(value: &T) -> Self {
52 Self { $($field: value.provide()),* }
53 }
54 }
55 };
56}
57
58/// Including a type here implements Provides<ThatType> for MyApp.
59///
60/// Struct definitions wrapped in the `inject!` macro get a From<T>
61/// implementation where T: Provides<U> for each field of type U in the struct.
62/// When those structs are provided as a component here, they will be
63/// constructed with the assumption that MyApp impl Provides<U> for each of
64/// those U's
65///
66/// All the types provided here are instantiated separately each time they are
67/// needed. If you want to support a singleton pattern, you need to construct
68/// the singletons in the constructor for this type and wrap them in an Arc.
69/// Then you can provide them in the "provided" section by cloning the Arc.
70///
71/// Open the main readme to see the following example in context.
72///
73/// ```rust ignore
74/// application! {
75/// self: MyApp
76///
77/// // init jobs are types implementing `Job` with a `run_once` function that
78/// // needs to run once during startup.
79/// // - constructed the same way as a component
80/// // - made available as a dependency, like a component
81/// // - wrap in curly braces for custom construction of an iterable of jobs.
82/// init [
83/// InitJob
84/// ]
85///
86/// // Services are types with a `run_forever` function that needs to run for
87/// // the entire lifetime of the application.
88/// // - constructed the same way as a component
89/// // - made available as a dependency, like a component
90/// // - registered as a service and spawned on startup.
91/// // - wrap in curly braces for custom construction of an iterable of
92/// // services.
93/// // - Use 'as WrapperType' if it needs to be wrapped in order to get
94/// // something that implements `Service`. wrapping uses WrapperType::from().
95/// services [
96/// MyService,
97/// JobToLoopForever as LoopingJobService,
98/// ]
99///
100/// // Components are items that will be provided as dependencies to anything
101/// // that needs it. This is similar to the types provided in the "provides"
102/// // section, except that components can be built exclusively from other
103/// // components and provided types, whereas "provides" items depend on other
104/// // state or logic.
105/// // - constructed via Type::from(MyApp). Use the inject! macro on the
106/// // type to make this possible.
107/// // - Use `as dyn SomeTrait` if you also want to provide the type as the
108/// // implementation for Arc<dyn SomeTrait>
109/// components [
110/// Component1,
111/// Component2,
112/// DatabaseRepository as dyn Repository,
113/// ]
114///
115/// // Use this when you want to provide a value of some type that needs to either:
116/// // - be constructed by some custom code you want to write here.
117/// // - depend on some state that was initialized in MyApp.
118/// //
119/// // Syntax: Provide a list of the types you want to provide, followed by the
120/// // expression that can be used to instantiate any of those types.
121/// // ```
122/// // TypeToProvide: { let x = self.get_x(); TypeToProvide::new(x) },
123/// // Arc<dyn Trait>, Arc<ConcreteType>: Arc::new(ConcreteType::default()),
124/// // ```
125/// provided {
126/// Arc<DatabaseConnectionPoolSingleton>: self.db_singleton.clone(),
127/// }
128/// }
129/// ```
130#[macro_export]
131macro_rules! application {
132 (
133 $self:ident: $Provider:ident
134 $(init [
135 $($JobIterable:block,)*
136 $($Job:ty $(as $JobAs:ty)?),*$(,)?
137 ])?
138 $(services [
139 $($SvcIterable:block,)*
140 $($Svc:ty $(as $SvcAs:ty)?),*$(,)?
141 ])?
142 $(components [
143 $($Component:ty $(as $($CompAs:ty)|+)?),+$(,)?
144 ])?
145 $(provided {
146 $($($Provided:ty),+: $logic:expr),+$(,)?
147 })?
148 ) => {
149 // Init
150 impl $crate::service_manager::Initialize for $Provider {
151 fn init(&$self) -> Vec<std::sync::Arc<dyn $crate::service::Job>> {
152 #[allow(unused_imports)]
153 use $crate::dependency_injection::Provides;
154 #[allow(unused_mut)]
155 let mut jobs: Vec<std::sync::Arc<dyn $crate::service::Job>> = vec![];
156 $(
157 $(for provided in $JobIterable {
158 jobs.push(provided);
159 })*
160 $(
161 let job = <$Job>::from($self);
162 $(let job = <$JobAs>::from(job);)?
163 jobs.push(std::sync::Arc::new(job));
164 )*
165 )?
166 jobs
167 }
168 }
169
170 // Services
171 impl $crate::service_manager::Serves for $Provider {
172 fn services(&$self) -> Vec<Box<dyn $crate::service::Service>> {
173 #[allow(unused_imports)]
174 use $crate::dependency_injection::Provides;
175 #[allow(unused_mut)]
176 let mut services: Vec<Box<dyn $crate::service::Service>> = vec![];
177 $(
178 $(for provided in $SvcIterable {
179 services.push(Box::new(provided));
180 })*
181 $(
182 let service = <$Svc>::from($self);
183 $(let service = <$SvcAs>::from(service);)?
184 services.push(Box::new(service));
185 )*
186 )?
187 services
188 }
189 }
190
191 // Components
192 $($(
193 impl $crate::dependency_injection::Provides<$Component> for $Provider {
194 fn provide(&self) -> $Component {
195 <$Component>::from(self)
196 }
197 }
198 $(
199 $(
200 impl $crate::dependency_injection::Provides<std::sync::Arc<$CompAs>> for $Provider {
201 fn provide(&self) -> std::sync::Arc<$CompAs> {
202 std::sync::Arc::new(<$Component>::from(self))
203 }
204 }
205 )+
206 )?
207 )*)?
208 // Provided
209 $($($(
210 impl $crate::dependency_injection::Provides<$Provided> for $Provider {
211 fn provide(&$self) -> $Provided {
212 $logic
213 }
214 }
215 )*)*)?
216 }
217}