1use futures::{
8 future::{self, BoxFuture, FutureExt},
9 pin_mut, select, Future,
10};
11
12use soil_service::Error as ServiceError;
13
14pub struct Signals(BoxFuture<'static, ()>);
18
19impl Signals {
20 pub fn future(self) -> BoxFuture<'static, ()> {
22 self.0
23 }
24
25 #[cfg(target_family = "unix")]
29 pub fn capture() -> std::result::Result<Self, ServiceError> {
30 use tokio::signal::unix::{signal, SignalKind};
31
32 let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?;
33 let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?;
34
35 Ok(Signals(
36 async move {
37 future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await;
38 }
39 .boxed(),
40 ))
41 }
42
43 #[cfg(not(unix))]
47 pub fn capture() -> Result<Self, ServiceError> {
48 use tokio::signal::ctrl_c;
49
50 Ok(Signals(
51 async move {
52 let _ = ctrl_c().await;
53 }
54 .boxed(),
55 ))
56 }
57
58 pub fn dummy() -> Self {
60 Self(future::pending().boxed())
61 }
62
63 pub async fn run_until_signal<F, E>(self, func: F) -> Result<(), E>
65 where
66 F: Future<Output = Result<(), E>> + future::FusedFuture,
67 E: std::error::Error + Send + Sync + 'static,
68 {
69 let signals = self.future().fuse();
70
71 pin_mut!(func, signals);
72
73 select! {
74 _ = signals => {},
75 res = func => res?,
76 }
77
78 Ok(())
79 }
80
81 pub async fn try_until_signal<F, T>(self, func: F) -> Result<T, ()>
83 where
84 F: Future<Output = T> + future::FusedFuture,
85 {
86 let signals = self.future().fuse();
87
88 pin_mut!(func, signals);
89
90 select! {
91 s = signals => Err(s),
92 res = func => Ok(res),
93 }
94 }
95}