watchctl 0.3.0

Process supervisor with wait, watch, and retry phases
use crate::error::{Error, Result};
use std::process::{ExitStatus, Stdio};
use tokio::process::{Child, Command};
use tracing::debug;

pub struct Process {
    child: Child,
}

impl Process {
    pub fn spawn(command: &[String]) -> Result<Self> {
        let (program, args) = command.split_first().ok_or_else(|| {
            Error::ProcessSpawn(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "command cannot be empty",
            ))
        })?;

        debug!("spawning process: {program} {:?}", args);

        let child = Command::new(program)
            .args(args)
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .map_err(Error::ProcessSpawn)?;

        Ok(Self { child })
    }

    pub async fn wait(&mut self) -> Result<ExitStatus> {
        self.child.wait().await.map_err(Error::Io)
    }

    pub async fn kill_and_wait(&mut self) -> Result<()> {
        self.child.kill().await.map_err(Error::Io)?;
        self.child.wait().await.map_err(Error::Io)?;
        Ok(())
    }

    #[allow(dead_code)]
    pub fn id(&self) -> Option<u32> {
        self.child.id()
    }
}