/
18_error_type_passthrough.rs
126 lines (106 loc) · 4.03 KB
/
18_error_type_passthrough.rs
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! This example shows to pass custom error types all the way through to the top,
//! to recover them from the return value of `handle_shutdown_requests`.
use env_logger::{Builder, Env};
use tokio::time::{sleep, Duration};
use tokio_graceful_shutdown::{
errors::GracefulShutdownError, IntoSubsystem, SubsystemHandle, Toplevel,
};
#[derive(Debug, thiserror::Error)]
enum MyError {
#[error("MyError.WithData: {0}")]
WithData(u32),
#[error("MyError.WithoutData")]
WithoutData,
}
async fn subsys1(_subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem1 started.");
sleep(Duration::from_millis(200)).await;
log::info!("Subsystem1 stopped.");
Err(MyError::WithData(42))
}
async fn subsys2(_subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem2 started.");
sleep(Duration::from_millis(200)).await;
log::info!("Subsystem2 stopped.");
Err(MyError::WithoutData)
}
async fn subsys3(_subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem3 started.");
sleep(Duration::from_millis(200)).await;
log::info!("Subsystem3 stopped.");
panic!("This subsystem panicked.");
}
async fn subsys4(_subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem4 started.");
sleep(Duration::from_millis(1000)).await;
log::info!("Subsystem4 stopped.");
// This subsystem would end normally but takes too long and therefore
// will time out.
Ok(())
}
async fn subsys5(_subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem5 started.");
sleep(Duration::from_millis(200)).await;
log::info!("Subsystem5 stopped.");
// This subsystem ended normally and should not show up in the list of
// subsystem errors.
Ok(())
}
struct Subsys6;
#[async_trait::async_trait]
impl IntoSubsystem<MyError, MyError> for Subsys6 {
async fn run(self, _subsys: SubsystemHandle<MyError>) -> Result<(), MyError> {
log::info!("Subsystem6 started.");
sleep(Duration::from_millis(200)).await;
log::info!("Subsystem6 stopped.");
Err(MyError::WithData(69))
}
}
#[tokio::main]
async fn main() -> Result<(), miette::Report> {
// Init logging
Builder::from_env(Env::default().default_filter_or("debug")).init();
// Create toplevel
let errors = Toplevel::<MyError>::new()
.start("Subsys1", subsys1)
.start("Subsys2", subsys2)
.start("Subsys3", subsys3)
.start("Subsys4", subsys4)
.start("Subsys5", subsys5)
.start("Subsys6", Subsys6.into_subsystem())
.catch_signals()
.handle_shutdown_requests::<GracefulShutdownError<MyError>>(Duration::from_millis(500))
.await;
if let Err(e) = &errors {
match e {
GracefulShutdownError::SubsystemsFailed(_) => {
log::warn!("Subsystems failed.")
}
GracefulShutdownError::ShutdownTimeout(_) => {
log::warn!("Shutdown timed out.")
}
};
for subsystem_error in e.get_subsystem_errors() {
match subsystem_error {
tokio_graceful_shutdown::SubsystemError::Failed(name, e) => {
log::warn!(" Subsystem '{}' failed.", name);
match e.get_error() {
MyError::WithData(data) => {
log::warn!(" It failed with MyError::WithData({})", data)
}
MyError::WithoutData => {
log::warn!(" It failed with MyError::WithoutData")
}
}
}
tokio_graceful_shutdown::SubsystemError::Cancelled(name) => {
log::warn!(" Subsystem '{}' was cancelled.", name)
}
tokio_graceful_shutdown::SubsystemError::Panicked(name) => {
log::warn!(" Subsystem '{}' panicked.", name)
}
}
}
};
Ok(errors?)
}