diff --git a/src/cli.rs b/src/cli.rs index 3926105..a7f5a8d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -40,6 +40,12 @@ pub enum Command { /// List timers #[clap(visible_alias = "l")] List, + /// Toggle timer + #[clap(visible_alias = "t")] + Toggle { + /// name of the timer to toggle + name: String + }, /// Remove a timer #[clap(visible_alias = "r")] Remove { @@ -83,6 +89,9 @@ pub enum PomodoroCommand { /// List the pomodoro settings and remaining duration #[clap(visible_alias = "l")] List, + /// Toggle pomodoro + #[clap(visible_alias = "t")] + Toggle } fn get_stream(socket_path: &String) -> Result { diff --git a/src/daemon.rs b/src/daemon.rs index e9cecbb..42e6ad3 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::fs::File; use std::path::Path; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::{ io::Write, os::unix::net::{UnixListener, UnixStream}, @@ -18,6 +18,7 @@ use std::{ #[derive(Debug, Serialize, Deserialize)] pub enum Command { Add(Box, Duration), + Toggle(Box), Remove(Box), List, PomodoroStart { @@ -28,6 +29,7 @@ pub enum Command { }, PomodoroRemove, PomodoroList, + PomodoroToggle, } #[derive(Debug, Serialize, Deserialize)] @@ -64,6 +66,8 @@ pub enum AnswerErr { TimerAlreadyExist(Box), #[error("No timer with the name '{}' exists", .0)] NoSuchTimer(Box), + #[error("No pomodoro running")] + NoPomodoro, } pub struct Daemon { @@ -110,7 +114,11 @@ impl Daemon { } fn has_timer(&mut self, name: &str) -> bool { - self.timers.iter().any(|other| other.name.as_ref() == name) + self.get_timer(name).is_some() + } + + fn get_timer(&mut self, name: &str) -> Option<&mut Timer> { + self.timers.iter_mut().find(|t| t.name.as_ref() == name) } fn handle_command(&mut self, command: Command) -> Result { @@ -137,6 +145,13 @@ impl Daemon { self.timers.push(timer); Ok(Answer::Ok) } + Command::Toggle(name) => match self.get_timer(&name) { + Some(timer) => { + timer.toggle(); + Ok(Answer::Ok) + } + None => Err(AnswerErr::NoSuchTimer(name)), + }, Command::Remove(name) => { if !self.has_timer(&name) { return Err(AnswerErr::NoSuchTimer(name)); @@ -160,6 +175,14 @@ impl Daemon { Ok(Answer::Ok) } Command::PomodoroList => Ok(Answer::Pomodoro(self.pomodoro.clone())), + Command::PomodoroToggle => match &mut self.pomodoro { + Some(ref mut pomodoro) => { + pomodoro.timer.toggle(); + Ok(Answer::Ok) + }, + None => Err(AnswerErr::NoPomodoro), + } + } } @@ -204,7 +227,7 @@ impl Daemon { } self.check_timers(); sleep(Duration::from_millis(100)); - }; + } Ok(()) } } diff --git a/src/main.rs b/src/main.rs index 5b118eb..5d6286d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ fn main() -> Result<(), anyhow::Error> { } CliCommand::List => DaemonCommand::List, CliCommand::Remove { name } => DaemonCommand::Remove(name.into_boxed_str()), + CliCommand::Toggle { name } => DaemonCommand::Toggle(name.into_boxed_str()), CliCommand::Pomodoro(pomodoro) => match pomodoro { PomodoroCommand::Start { work, @@ -36,6 +37,7 @@ fn main() -> Result<(), anyhow::Error> { }, PomodoroCommand::Remove => DaemonCommand::PomodoroRemove, PomodoroCommand::List => DaemonCommand::PomodoroList, + PomodoroCommand::Toggle => DaemonCommand::PomodoroToggle, }, }; let answer = send_command(&args.socket, daemon_command)?; diff --git a/src/timer.rs b/src/timer.rs index 44c929c..a613fe0 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -38,6 +38,13 @@ pub struct Timer { #[serde(with = "approx_instant")] start: Instant, duration: Duration, + state: State, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +pub enum State { + Running, + Paused, } impl Display for Timer { @@ -57,6 +64,7 @@ impl Timer { name, start: Instant::now(), duration, + state: State::Running, } } @@ -66,10 +74,14 @@ impl Timer { /// Returns the remaining duration rounded to seconds of this [`Timer`]. pub fn remaining(&self) -> Duration { + if self.state == State::Paused { + return self.duration; + }; let exact = self.duration - (Instant::now() - self.start); Duration::from_secs(exact.as_secs()) } + /// Logs or notifies timer expiration pub fn handle_expiration(&self, notify: bool) { let msg = format!("Timer {} has expired!", self.name); println!("{}", &msg); @@ -77,4 +89,15 @@ impl Timer { send_notifictation(msg.as_str()); } } + + /// Toggles the timers state + pub fn toggle(&mut self) { + if self.state != State::Paused { + self.duration = self.remaining(); + self.state = State::Paused; + } else { + self.start = Instant::now(); + self.state = State::Running; + } + } }