use std::time::Duration; use crate::signal::process::SubSignal; /// The outcome to execute when an action is triggered. /// /// Logic against the state of the command should be expressed using these variants, rather than /// inside the action handler, as it ensures the state of the command is always the latest available /// when the outcome is executed. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Outcome { /// Stop processing this action silently. DoNothing, /// If the command is running, stop it. /// /// This should be used with an `IfRunning`, and will warn if the command is not running. Stop, /// If the command isn't running, start it. /// /// This should be used with an `IfRunning`, and will warn if the command is running. Start, /// Wait for command completion. /// /// Does nothing if the command isn't running. Wait, /// Sleep for some duration. Sleep(Duration), /// Send this signal to the command. /// /// This does not wait for the command to complete. Signal(SubSignal), /// Clear the (terminal) screen. Clear, /// Reset the (terminal) screen. /// /// This invokes (in order): [`WindowsCooked`][clearscreen::ClearScreen::WindowsCooked], /// [`WindowsVt`][clearscreen::ClearScreen::WindowsVt], /// [`VtLeaveAlt`][clearscreen::ClearScreen::VtLeaveAlt], /// [`VtWellDone`][clearscreen::ClearScreen::VtWellDone], /// and [the default clear][clearscreen::ClearScreen::default()]. Reset, /// Exit watchexec. Exit, /// When command is running, do the first, otherwise the second. IfRunning(Box, Box), /// Do both outcomes in order. Both(Box, Box), } impl Default for Outcome { fn default() -> Self { Self::DoNothing } } impl Outcome { /// Convenience function to create an outcome conditional on the state of the subprocess. #[must_use] pub fn if_running(then: Self, otherwise: Self) -> Self { Self::IfRunning(Box::new(then), Box::new(otherwise)) } /// Convenience function to create a sequence of outcomes. #[must_use] pub fn both(one: Self, two: Self) -> Self { Self::Both(Box::new(one), Box::new(two)) } /// Convenience function to wait for the subprocess to complete before executing the outcome. #[must_use] pub fn wait(and_then: Self) -> Self { Self::Both(Box::new(Self::Wait), Box::new(and_then)) } /// Resolves the outcome given the current state of the subprocess. #[must_use] pub fn resolve(self, is_running: bool) -> Self { match (is_running, self) { (true, Self::IfRunning(then, _)) => then.resolve(true), (false, Self::IfRunning(_, otherwise)) => otherwise.resolve(false), (ir, Self::Both(one, two)) => Self::both(one.resolve(ir), two.resolve(ir)), (_, other) => other, } } } #[cfg(test)] mod test { use super::*; #[test] fn simple_if_running() { assert_eq!( Outcome::if_running(Outcome::Stop, Outcome::Start).resolve(true), Outcome::Stop ); assert_eq!( Outcome::if_running(Outcome::Stop, Outcome::Start).resolve(false), Outcome::Start ); } #[test] fn simple_passthrough() { assert_eq!(Outcome::Wait.resolve(true), Outcome::Wait); assert_eq!(Outcome::Clear.resolve(false), Outcome::Clear); } #[test] fn nested_if_runnings() { assert_eq!( Outcome::both( Outcome::if_running(Outcome::Stop, Outcome::Start), Outcome::if_running(Outcome::Wait, Outcome::Exit) ) .resolve(true), Outcome::Both(Box::new(Outcome::Stop), Box::new(Outcome::Wait)) ); } }