feat: add pausing of timers
parent
8bbb6c9113
commit
bd91ea9d90
|
@ -40,6 +40,12 @@ pub enum Command {
|
||||||
/// List timers
|
/// List timers
|
||||||
#[clap(visible_alias = "l")]
|
#[clap(visible_alias = "l")]
|
||||||
List,
|
List,
|
||||||
|
/// Toggle timer
|
||||||
|
#[clap(visible_alias = "t")]
|
||||||
|
Toggle {
|
||||||
|
/// name of the timer to toggle
|
||||||
|
name: String
|
||||||
|
},
|
||||||
/// Remove a timer
|
/// Remove a timer
|
||||||
#[clap(visible_alias = "r")]
|
#[clap(visible_alias = "r")]
|
||||||
Remove {
|
Remove {
|
||||||
|
@ -83,6 +89,9 @@ pub enum PomodoroCommand {
|
||||||
/// List the pomodoro settings and remaining duration
|
/// List the pomodoro settings and remaining duration
|
||||||
#[clap(visible_alias = "l")]
|
#[clap(visible_alias = "l")]
|
||||||
List,
|
List,
|
||||||
|
/// Toggle pomodoro
|
||||||
|
#[clap(visible_alias = "t")]
|
||||||
|
Toggle
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_stream(socket_path: &String) -> Result<UnixStream> {
|
fn get_stream(socket_path: &String) -> Result<UnixStream> {
|
||||||
|
|
|
@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
os::unix::net::{UnixListener, UnixStream},
|
os::unix::net::{UnixListener, UnixStream},
|
||||||
|
@ -18,6 +18,7 @@ use std::{
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Add(Box<str>, Duration),
|
Add(Box<str>, Duration),
|
||||||
|
Toggle(Box<str>),
|
||||||
Remove(Box<str>),
|
Remove(Box<str>),
|
||||||
List,
|
List,
|
||||||
PomodoroStart {
|
PomodoroStart {
|
||||||
|
@ -28,6 +29,7 @@ pub enum Command {
|
||||||
},
|
},
|
||||||
PomodoroRemove,
|
PomodoroRemove,
|
||||||
PomodoroList,
|
PomodoroList,
|
||||||
|
PomodoroToggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -64,6 +66,8 @@ pub enum AnswerErr {
|
||||||
TimerAlreadyExist(Box<str>),
|
TimerAlreadyExist(Box<str>),
|
||||||
#[error("No timer with the name '{}' exists", .0)]
|
#[error("No timer with the name '{}' exists", .0)]
|
||||||
NoSuchTimer(Box<str>),
|
NoSuchTimer(Box<str>),
|
||||||
|
#[error("No pomodoro running")]
|
||||||
|
NoPomodoro,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Daemon {
|
pub struct Daemon {
|
||||||
|
@ -110,7 +114,11 @@ impl Daemon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_timer(&mut self, name: &str) -> bool {
|
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<Answer, AnswerErr> {
|
fn handle_command(&mut self, command: Command) -> Result<Answer, AnswerErr> {
|
||||||
|
@ -137,6 +145,13 @@ impl Daemon {
|
||||||
self.timers.push(timer);
|
self.timers.push(timer);
|
||||||
Ok(Answer::Ok)
|
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) => {
|
Command::Remove(name) => {
|
||||||
if !self.has_timer(&name) {
|
if !self.has_timer(&name) {
|
||||||
return Err(AnswerErr::NoSuchTimer(name));
|
return Err(AnswerErr::NoSuchTimer(name));
|
||||||
|
@ -160,6 +175,14 @@ impl Daemon {
|
||||||
Ok(Answer::Ok)
|
Ok(Answer::Ok)
|
||||||
}
|
}
|
||||||
Command::PomodoroList => Ok(Answer::Pomodoro(self.pomodoro.clone())),
|
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();
|
self.check_timers();
|
||||||
sleep(Duration::from_millis(100));
|
sleep(Duration::from_millis(100));
|
||||||
};
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
}
|
}
|
||||||
CliCommand::List => DaemonCommand::List,
|
CliCommand::List => DaemonCommand::List,
|
||||||
CliCommand::Remove { name } => DaemonCommand::Remove(name.into_boxed_str()),
|
CliCommand::Remove { name } => DaemonCommand::Remove(name.into_boxed_str()),
|
||||||
|
CliCommand::Toggle { name } => DaemonCommand::Toggle(name.into_boxed_str()),
|
||||||
CliCommand::Pomodoro(pomodoro) => match pomodoro {
|
CliCommand::Pomodoro(pomodoro) => match pomodoro {
|
||||||
PomodoroCommand::Start {
|
PomodoroCommand::Start {
|
||||||
work,
|
work,
|
||||||
|
@ -36,6 +37,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
},
|
},
|
||||||
PomodoroCommand::Remove => DaemonCommand::PomodoroRemove,
|
PomodoroCommand::Remove => DaemonCommand::PomodoroRemove,
|
||||||
PomodoroCommand::List => DaemonCommand::PomodoroList,
|
PomodoroCommand::List => DaemonCommand::PomodoroList,
|
||||||
|
PomodoroCommand::Toggle => DaemonCommand::PomodoroToggle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let answer = send_command(&args.socket, daemon_command)?;
|
let answer = send_command(&args.socket, daemon_command)?;
|
||||||
|
|
23
src/timer.rs
23
src/timer.rs
|
@ -38,6 +38,13 @@ pub struct Timer {
|
||||||
#[serde(with = "approx_instant")]
|
#[serde(with = "approx_instant")]
|
||||||
start: Instant,
|
start: Instant,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub enum State {
|
||||||
|
Running,
|
||||||
|
Paused,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Timer {
|
impl Display for Timer {
|
||||||
|
@ -57,6 +64,7 @@ impl Timer {
|
||||||
name,
|
name,
|
||||||
start: Instant::now(),
|
start: Instant::now(),
|
||||||
duration,
|
duration,
|
||||||
|
state: State::Running,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,10 +74,14 @@ impl Timer {
|
||||||
|
|
||||||
/// Returns the remaining duration rounded to seconds of this [`Timer`].
|
/// Returns the remaining duration rounded to seconds of this [`Timer`].
|
||||||
pub fn remaining(&self) -> Duration {
|
pub fn remaining(&self) -> Duration {
|
||||||
|
if self.state == State::Paused {
|
||||||
|
return self.duration;
|
||||||
|
};
|
||||||
let exact = self.duration - (Instant::now() - self.start);
|
let exact = self.duration - (Instant::now() - self.start);
|
||||||
Duration::from_secs(exact.as_secs())
|
Duration::from_secs(exact.as_secs())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs or notifies timer expiration
|
||||||
pub fn handle_expiration(&self, notify: bool) {
|
pub fn handle_expiration(&self, notify: bool) {
|
||||||
let msg = format!("Timer {} has expired!", self.name);
|
let msg = format!("Timer {} has expired!", self.name);
|
||||||
println!("{}", &msg);
|
println!("{}", &msg);
|
||||||
|
@ -77,4 +89,15 @@ impl Timer {
|
||||||
send_notifictation(msg.as_str());
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue