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}