1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
use anyhow::Result;
use serde::de::DeserializeOwned;
use shared::{
config::Settings,
log::{error, info},
};
use std::{future::Future, pin::Pin};
use tokio::{signal::{unix::SignalKind}, sync::oneshot};
pub type AnyhowResultFuture<T> = Pin<Box<dyn Future<Output = Result<T>>>>;
pub trait Component: Send + Sync + 'static + Sized {
type Config: Default + Clone + DeserializeOwned;
const SERVICE_NAME: &'static str;
fn start(
&self,
settings: Settings<Self::Config>,
stop: oneshot::Receiver<()>,
) -> AnyhowResultFuture<()>;
fn new() -> Self;
fn _internal_start(self) -> AnyhowResultFuture<()> {
Box::pin(async move {
pretty_env_logger::init();
let settings = Settings::<Self::Config>::new(Self::SERVICE_NAME);
let (stop, stop_channel) = oneshot::channel();
// Start the grpc healthcheck
tokio::spawn(async move {});
// Start the prometheus monitoring job
tokio::spawn(async move {});
tokio::spawn(async move {
match tokio::signal::unix::signal(SignalKind::terminate()).unwrap().recv().await {
Some(()) => {
info!("Stopping program.");
stop.send(()).unwrap();
}
None => {
error!("Unable to listen for shutdown signal");
// we also shut down in case of error
}
}
});
self.start(settings?, stop_channel).await
})
}
}
#[macro_export]
macro_rules! ignite {
($c:ty) => {
#[allow(dead_code)]
fn main() -> anyhow::Result<()> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(Box::new(<$c as Component>::new())._internal_start())?;
Ok(())
}
};
}
#[cfg(test)]
mod test {
use serde::Deserialize;
use tokio::sync::oneshot;
use crate::Component;
#[derive(Clone, Copy)]
struct TestComponent {}
#[derive(Default, Clone, Deserialize, Copy)]
struct TestComponentConfig {}
impl Component for TestComponent {
type Config = TestComponentConfig;
const SERVICE_NAME: &'static str = "test_component";
fn start(
&self,
_settings: shared::config::Settings<Self::Config>,
_stop: oneshot::Receiver<()>,
) -> crate::AnyhowResultFuture<()> {
Box::pin(async move { Ok(()) })
}
fn new() -> Self {
Self {}
}
}
ignite!(TestComponent);
}
|