watchctl 0.4.0

Process supervisor with wait, watch, and retry phases
#[derive(Debug)]
pub struct Termination {
    pub name: &'static str,
    pub exit_code: u8,
}

#[cfg(unix)]
use tokio::signal::unix::Signal;

#[cfg(unix)]
async fn recv_opt(handler: &mut Option<Signal>) {
    use std::future::pending;
    match handler {
        Some(s) => {
            s.recv().await;
        }
        None => pending::<()>().await,
    }
}

#[cfg(unix)]
pub struct TerminationListener {
    interrupt: Option<Signal>,
    terminate: Option<Signal>,
    hangup: Option<Signal>,
}

#[cfg(unix)]
impl TerminationListener {
    pub fn new() -> Self {
        use tokio::signal::unix::{SignalKind, signal};
        Self {
            interrupt: signal(SignalKind::interrupt()).ok(),
            terminate: signal(SignalKind::terminate()).ok(),
            hangup: signal(SignalKind::hangup()).ok(),
        }
    }

    pub async fn recv(&mut self) -> Termination {
        use tokio::select;
        let interrupt = &mut self.interrupt;
        let terminate = &mut self.terminate;
        let hangup = &mut self.hangup;
        select! {
            _ = recv_opt(interrupt) => Termination { name: "SIGINT", exit_code: 130 },
            _ = recv_opt(terminate) => Termination { name: "SIGTERM", exit_code: 143 },
            _ = recv_opt(hangup) => Termination { name: "SIGHUP", exit_code: 129 },
        }
    }
}

#[cfg(windows)]
use tokio::signal::windows::{CtrlBreak, CtrlC, CtrlClose, CtrlLogoff, CtrlShutdown};

#[cfg(windows)]
pub struct TerminationListener {
    interrupt: Option<CtrlC>,
    brk: Option<CtrlBreak>,
    close: Option<CtrlClose>,
    logoff: Option<CtrlLogoff>,
    shutdown: Option<CtrlShutdown>,
}

#[cfg(windows)]
impl TerminationListener {
    pub fn new() -> Self {
        use tokio::signal::windows::{ctrl_break, ctrl_c, ctrl_close, ctrl_logoff, ctrl_shutdown};
        Self {
            interrupt: ctrl_c().ok(),
            brk: ctrl_break().ok(),
            close: ctrl_close().ok(),
            logoff: ctrl_logoff().ok(),
            shutdown: ctrl_shutdown().ok(),
        }
    }

    pub async fn recv(&mut self) -> Termination {
        use std::future::pending;
        use tokio::select;
        let interrupt = &mut self.interrupt;
        let brk = &mut self.brk;
        let close = &mut self.close;
        let logoff = &mut self.logoff;
        let shutdown = &mut self.shutdown;
        select! {
            _ = async { match interrupt { Some(s) => { s.recv().await; }, None => pending::<()>().await } } => Termination { name: "CTRL_C", exit_code: 130 },
            _ = async { match brk { Some(s) => { s.recv().await; }, None => pending::<()>().await } } => Termination { name: "CTRL_BREAK", exit_code: 130 },
            _ = async { match close { Some(s) => { s.recv().await; }, None => pending::<()>().await } } => Termination { name: "CTRL_CLOSE", exit_code: 130 },
            _ = async { match logoff { Some(s) => { s.recv().await; }, None => pending::<()>().await } } => Termination { name: "CTRL_LOGOFF", exit_code: 130 },
            _ = async { match shutdown { Some(s) => { s.recv().await; }, None => pending::<()>().await } } => Termination { name: "CTRL_SHUTDOWN", exit_code: 130 },
        }
    }
}

impl Default for TerminationListener {
    fn default() -> Self {
        Self::new()
    }
}