tari_service_framework/
initializer.rs

1// Copyright 2019 The Tari Project
2//
3// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4// following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7// disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10// following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13// products derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23use crate::context::ServiceInitializerContext;
24use futures::{Future, FutureExt};
25use std::pin::Pin;
26
27pub type ServiceInitializationError = anyhow::Error;
28
29/// Implementors of this trait will initialize a service
30/// The `StackBuilder` builds impls of this trait.
31pub trait ServiceInitializer {
32    /// The future returned from the initialize function
33    type Future: Future<Output = Result<(), ServiceInitializationError>>;
34
35    /// Async initialization code for a service
36    fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future;
37
38    /// Create a boxed version of this ServiceInitializer.
39    fn boxed(self) -> BoxedServiceInitializer
40    where
41        Self: Sized + Send + 'static,
42        Self::Future: Send + 'static,
43    {
44        BoxedServiceInitializer::new(self)
45    }
46}
47
48/// Implementation of ServiceInitializer for any function matching the signature of `ServiceInitializer::initialize`
49/// This allows the following "short-hand" syntax to be used:
50///
51/// ```edition2018
52/// # use tari_service_framework::ServiceInitializerContext;
53/// # use tokio::runtime;
54/// let my_initializer = |executor: runtime::Handle, context: ServiceInitializerContext| {
55///     // initialization code
56///     futures::future::ready(Result::<_, ()>::Ok(()))
57/// };
58/// ```
59impl<TFunc, TFut> ServiceInitializer for TFunc
60where
61    TFunc: FnMut(ServiceInitializerContext) -> TFut,
62    TFut: Future<Output = Result<(), ServiceInitializationError>>,
63{
64    type Future = TFut;
65
66    fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future {
67        (self)(context)
68    }
69}
70
71/// An implementation of `ServiceInitializer` that wraps an `impl FnOnce`
72pub struct InitializerFn<TFunc>(Option<TFunc>);
73
74impl<TFunc> InitializerFn<TFunc> {
75    pub fn new(f: TFunc) -> Self {
76        Self(Some(f))
77    }
78}
79
80impl<TFunc, TFut> ServiceInitializer for InitializerFn<TFunc>
81where
82    TFunc: FnOnce(ServiceInitializerContext) -> TFut,
83    TFut: Future<Output = Result<(), ServiceInitializationError>>,
84{
85    type Future = TFut;
86
87    fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future {
88        let f = self.0.take().expect("initializer called more than once");
89        (f)(context)
90    }
91}
92
93//---------------------------------- Boxed Service Initializer --------------------------------------------//
94// The following code is essentially a substitute for async trait functions. Any initializer can
95// converted to the boxed form by using ServiceInitializer::boxed(). This is done for you when
96// using `StackBuilder::add_initializer`.
97
98/// A pinned, boxed form of the future resulting from a boxed ServiceInitializer
99type ServiceInitializationFuture = Pin<Box<dyn Future<Output = Result<(), ServiceInitializationError>> + Send>>;
100
101/// This trait mirrors the ServiceInitializer trait, with the exception
102/// of always returning a boxed future (aliased ServiceInitializationFuture type),
103/// therefore it does not need the `Future` associated type. This makes it
104/// possible to store a boxed dyn `AbstractServiceInitializer<TName, TExec>`.
105pub trait AbstractServiceInitializer {
106    fn initialize(&mut self, context: ServiceInitializerContext) -> ServiceInitializationFuture;
107}
108
109/// AbstractServiceInitializer impl for every T: ServiceInitializer.
110impl<T> AbstractServiceInitializer for T
111where
112    T: ServiceInitializer,
113    T::Future: Send + 'static,
114{
115    fn initialize(&mut self, context: ServiceInitializerContext) -> ServiceInitializationFuture {
116        let initialization = self.initialize(context);
117        initialization.boxed() as ServiceInitializationFuture
118    }
119}
120
121/// A concrete boxed version of a ServiceInitializer. This makes it possible
122/// to have a collection of ServiceInitializers which return various boxed future types.
123/// This type is used in StackBuilder's internal vec.
124pub struct BoxedServiceInitializer {
125    inner: Box<dyn AbstractServiceInitializer + Send + 'static>,
126}
127
128impl BoxedServiceInitializer {
129    pub(super) fn new<T>(initializer: T) -> Self
130    where
131        T: ServiceInitializer + Send + 'static,
132        T::Future: Send + 'static,
133    {
134        Self {
135            inner: Box::new(initializer),
136        }
137    }
138}
139
140impl ServiceInitializer for BoxedServiceInitializer {
141    type Future = ServiceInitializationFuture;
142
143    fn initialize(&mut self, context: ServiceInitializerContext) -> Self::Future {
144        self.inner.initialize(context)
145    }
146}