diff --git a/src/app.rs b/src/app.rs index 318a3e0..6e831f5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -27,12 +27,17 @@ pub(crate) enum Mode { }, /// The timer mode displays the remaining time until the timer is finished. Timer { + /// Initial duration for timer, value can be 10s for 10 seconds, 1m for 1 minute, etc. #[clap(short, long, value_parser = parse_duration, default_value = "5m")] duration: Duration, /// Hide milliseconds #[clap(long = "no-millis", short = 'M', takes_value = false)] no_millis: bool, + + /// Command to run when the timer ends + #[clap(long, short, multiple = true, allow_hyphen_values = true)] + execute: Vec, }, /// The stopwatch mode displays the elapsed time since it was started. Stopwatch, @@ -79,13 +84,20 @@ impl App { Mode::Timer { duration, no_millis, + execute, } => { let format = if *no_millis { DurationFormat::HourMinSec } else { DurationFormat::HourMinSecDeci }; - self.timer = Some(Timer::new(duration.to_owned(), self.size, style, format)); + self.timer = Some(Timer::new( + duration.to_owned(), + self.size, + style, + format, + execute.to_owned(), + )); } Mode::Stopwatch => { self.stopwatch = Some(Stopwatch::new(self.size, style)); diff --git a/src/app/modes/timer.rs b/src/app/modes/timer.rs index 6865e6e..29c9cdb 100644 --- a/src/app/modes/timer.rs +++ b/src/app/modes/timer.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, process::Command}; + use chrono::{DateTime, Duration, Local}; use clock_tui::bricks_text::BricksText; use tui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget}; @@ -7,19 +9,29 @@ use super::{format_duration, render_centered, DurationFormat}; pub struct Timer { pub size: u16, pub style: Style, + pub execute: Vec, format: DurationFormat, duration: Duration, ended_at: Option>, + execute_result: RefCell>, } impl Timer { - pub(crate) fn new(duration: Duration, size: u16, style: Style, format: DurationFormat) -> Self { + pub(crate) fn new( + duration: Duration, + size: u16, + style: Style, + format: DurationFormat, + execute: Vec, + ) -> Self { Self { duration, size, + execute, style, format, ended_at: Some(Local::now() + duration), + execute_result: RefCell::new(None), } } @@ -33,8 +45,8 @@ impl Timer { self.duration = Duration::zero(); } else { self.duration = end_at - Local::now(); + self.ended_at = None; } - self.ended_at = None; } } @@ -54,10 +66,34 @@ impl Timer { } } +fn execute(execute: &Vec) -> String { + let mut cmd = Command::new("sh"); + cmd.arg("-c"); + let cmd_str = execute.join(" "); + cmd.arg(cmd_str); + let output = cmd.output(); + match output { + Ok(output) => { + if !output.status.success() { + format!("[ERROR] {}", String::from_utf8_lossy(&output.stderr)) + } else { + format!("[SUCCEED] {}", String::from_utf8_lossy(&output.stdout)) + } + } + Err(e) => { + format!("[FAILED] {}", e) + } + } +} + impl Widget for &Timer { fn render(self, area: Rect, buf: &mut Buffer) { let remaining_time = self.remaining_time(); let time_str = if remaining_time < Duration::zero() { + if self.execute.len() > 0 && self.execute_result.borrow().is_none() { + let result = execute(&self.execute); + *self.execute_result.borrow_mut() = Some(result); + } if remaining_time.num_seconds() % 2 == 0 { return; } else { @@ -71,7 +107,7 @@ impl Widget for &Timer { let footer = if self.is_paused() { Some("PAUSED (press to resume)".to_string()) } else { - None + self.execute_result.borrow().clone() }; render_centered(area, buf, &text, None, footer); }