baywatch 0.0.2

Watch files, execute commands on changes.
use anyhow::{anyhow, Result};
use std::{
    env,
    process::{Command, Stdio},
};

use crate::watching::ChangeEvent;

pub trait EventHandler {
    fn handle(&self, event: ChangeEvent) -> Result<()>;
}

pub struct CommandEventHandler {
    command: Vec<String>,
    status: bool,
}

impl CommandEventHandler {
    pub fn new(command: Vec<String>, status: bool) -> Self {
        CommandEventHandler { command, status }
    }

    pub fn new_shell(command: Vec<String>, status: bool) -> Self {
        let shell = env::var("SHELL").unwrap_or("sh".to_owned());
        let cmd = vec![shell, "-c".to_owned(), command.join(" ")];
        Self::new(cmd, status)
    }
}

impl EventHandler for CommandEventHandler {
    fn handle(&self, event: ChangeEvent) -> Result<()> {
        log::debug!("{:?}, command: {:?}", event, self.command);

        let main_command = self
            .command
            .first()
            .ok_or_else(|| anyhow!("empty command"))?;
        let mut cmd = Command::new(main_command)
            .args(&self.command[1..])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap();

        let status = cmd.wait()?;
        if self.status {
            if let Some(code) = status.code() {
                println!("Exited with: {code}")
            }
        };

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_non_shell_handler_executes_correctly() {
        let handler = CommandEventHandler::new(vec!["ls".to_owned()], false);
        let event = ChangeEvent;
        handler.handle(event).expect("handle must succeed");
    }

    #[test]
    fn test_shell_handler_executes_correctly() {
        let handler = CommandEventHandler::new(vec!["echo".to_owned(), "hi".to_owned()], false);
        let event = ChangeEvent;
        handler.handle(event).expect("handle must succeed");
    }

    #[test]
    fn test_handler_fails_without_any_command() {
        let handler = CommandEventHandler::new(Vec::new(), false);
        let event = ChangeEvent;
        handler
            .handle(event)
            .expect_err("handle return error when command is empty");
    }
}