use std::io;
#[cfg(unix)]
pub(crate) async fn termination() -> io::Result<&'static str> {
Ok(Watcher::install()?.caught().await)
}
#[cfg(not(unix))]
pub(crate) async fn termination() -> io::Result<&'static str> {
tokio::signal::ctrl_c().await?;
Ok("Ctrl+C")
}
#[cfg(unix)]
struct Watcher {
interrupt: tokio::signal::unix::Signal,
terminate: tokio::signal::unix::Signal,
hangup: tokio::signal::unix::Signal,
}
#[cfg(unix)]
impl Watcher {
fn install() -> io::Result<Self> {
use tokio::signal::unix::{SignalKind, signal};
Ok(Self {
interrupt: signal(SignalKind::interrupt())?,
terminate: signal(SignalKind::terminate())?,
hangup: signal(SignalKind::hangup())?,
})
}
async fn caught(mut self) -> &'static str {
tokio::select! {
_ = self.interrupt.recv() => "SIGINT",
_ = self.terminate.recv() => "SIGTERM",
_ = self.hangup.recv() => "SIGHUP",
}
}
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
#[tokio::test]
async fn a_signal_resolves_the_watcher() {
let watcher = Watcher::install().unwrap();
let sent = std::process::Command::new("kill")
.args(["-s", "HUP", &std::process::id().to_string()])
.status()
.unwrap();
assert!(sent.success(), "kill must deliver the signal");
let name = tokio::time::timeout(std::time::Duration::from_secs(5), watcher.caught())
.await
.expect("the signal must arrive");
assert_eq!(name, "SIGHUP");
}
}