From 0c954962cc23418f17a652f1926ca95ea0db454e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Sat, 29 Jul 2023 12:52:50 +0200 Subject: [PATCH] feat: better durations and allow stopping pomodoro --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/cli.rs | 26 +++++++++++++++++--------- src/daemon.rs | 34 +++++++++++++++++++++++----------- src/main.rs | 34 +++++++++++++++++----------------- src/pomodoro.rs | 20 +++++++++++--------- src/timer.rs | 8 +++++--- 7 files changed, 81 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ece2c6..90edaa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -562,6 +562,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "indexmap" version = "2.0.0" @@ -1117,6 +1123,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "humantime", "notify-rust", "serde", "serde_cbor", diff --git a/Cargo.toml b/Cargo.toml index 340fb51..66307d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] anyhow = "1.0.71" clap = { version = "4.3.4", features = ["derive"] } +humantime = "2.1.0" notify-rust = "4.8.0" serde = { version = "1.0.164", features = ["derive"] } serde_cbor = "0.11.2" diff --git a/src/cli.rs b/src/cli.rs index 3e61c18..5053962 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,6 +3,7 @@ use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use std::net::Shutdown; use std::os::unix::net::UnixStream; +use std::time::Duration; #[derive(Debug, Parser)] #[command(name = "timers")] @@ -24,22 +25,29 @@ pub enum Command { }, Add { name: String, - duration_seconds: u64, + duration: humantime::Duration, }, List, Remove { name: String, }, - Pomodoro { - #[clap(default_value_t = 25)] - work_minutes: u64, - #[clap(default_value_t = 5)] - pause_minutes: u64, - #[clap(default_value_t = 10)] - long_pause_minutes: u64, + #[command(subcommand)] + Pomodoro(PomodoroCommand) +} + +#[derive(Debug, Subcommand)] +pub enum PomodoroCommand { + Start { + #[clap(default_value_t = Duration::from_secs(25 * 60).into())] + work: humantime::Duration, + #[clap(default_value_t = Duration::from_secs(5 * 60).into())] + pause: humantime::Duration, + #[clap(default_value_t = Duration::from_secs(10 * 60).into())] + long_pause: humantime::Duration, #[clap(default_value_t = 3)] pauses_till_long: u64, - } + }, + Stop, } fn get_stream(socket_path: &String) -> Result { diff --git a/src/daemon.rs b/src/daemon.rs index c35ee21..95b6962 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -16,12 +16,13 @@ pub enum Command { Add(Box, Duration), Remove(Box), List, - Pomodoro { + PomodoroStart { work: Duration, pause: Duration, long_pause: Duration, pauses_till_long: u64, }, + PomodoroStop } #[derive(Debug, Serialize, Deserialize)] @@ -100,7 +101,14 @@ impl Daemon { if self.notify { match Notification::new() .summary("󰀠 Timers") - .body(format!("Started timer {} for {:?}", &name, duration).as_str()) + .body( + format!( + "Started timer {} for {}", + &name, + humantime::format_duration(duration) + ) + .as_str(), + ) .show() { Ok(_) => println!("Sent notification sucessfully."), @@ -120,23 +128,27 @@ impl Daemon { .retain(|other| other.name.as_ref() != name.as_ref()); Ok(Answer::Ok) } - Command::Pomodoro { + Command::PomodoroStart { work, pause, long_pause, pauses_till_long, } => { - match Notification::new() - .summary("󰀠 Timers") - .body("Started pomodoro.") - .show() - { - Ok(_) => println!("Sent notification sucessfully."), - Err(_) => println!("Failed to send notification."), - }; + match Notification::new() + .summary("󰀠 Timers") + .body("Started pomodoro.") + .show() + { + Ok(_) => println!("Sent notification sucessfully."), + Err(_) => println!("Failed to send notification."), + }; self.pomodoro = Some(Pomodoro::new(work, pause, long_pause, pauses_till_long)); Ok(Answer::Ok) } + Command::PomodoroStop => { + self.pomodoro = None; + Ok(Answer::Ok) + }, } } diff --git a/src/main.rs b/src/main.rs index 7890f75..4e81bdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,34 +3,34 @@ pub mod daemon; pub mod pomodoro; pub mod timer; -use std::time::Duration; - use crate::cli::{send_command, Cli, Command as CliCommand}; use crate::daemon::{Command as DaemonCommand, Daemon}; use anyhow::Result; use clap::Parser; +use cli::PomodoroCommand; fn main() -> Result<()> { let args = Cli::parse(); let daemon_command = match args.command { CliCommand::Daemon { notify } => return Daemon::new(args.socket, notify)?.run(), - CliCommand::Add { - name, - duration_seconds, - } => DaemonCommand::Add(name.into_boxed_str(), Duration::from_secs(duration_seconds)), + CliCommand::Add { name, duration } => { + DaemonCommand::Add(name.into_boxed_str(), duration.into()) + } CliCommand::List => DaemonCommand::List, CliCommand::Remove { name } => DaemonCommand::Remove(name.into_boxed_str()), - CliCommand::Pomodoro { - work_minutes, - pause_minutes, - long_pause_minutes, - pauses_till_long, - - } => DaemonCommand::Pomodoro{ - work: Duration::from_secs(work_minutes * 60), - pause: Duration::from_secs(pause_minutes * 60), - long_pause: Duration::from_secs(long_pause_minutes * 60), - pauses_till_long + CliCommand::Pomodoro(pomodoro) => match pomodoro { + PomodoroCommand::Start { + work, + pause, + long_pause, + pauses_till_long, + } => DaemonCommand::PomodoroStart { + work: work.into(), + pause: pause.into(), + long_pause: long_pause.into(), + pauses_till_long, + }, + PomodoroCommand::Stop => DaemonCommand::PomodoroStop, }, }; send_command(&args.socket, daemon_command) diff --git a/src/pomodoro.rs b/src/pomodoro.rs index 7344ddf..da78e06 100644 --- a/src/pomodoro.rs +++ b/src/pomodoro.rs @@ -19,12 +19,12 @@ impl Display for Pomodoro { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Pomodoro ({:?}, {:?}, {:?}) currently {} with {:?} remaining.", - self.work, - self.pause, - self.long_pause, + "Pomodoro ({}, {}, {}) currently {} with {} remaining.", + humantime::format_duration(self.work), + humantime::format_duration(self.pause), + humantime::format_duration(self.long_pause), self.status, - self.timer.remaining() + humantime::format_duration(self.timer.remaining()) ) } } @@ -33,13 +33,15 @@ impl Display for Pomodoro { enum Status { Working, Pausing, + LongPause, } impl Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Status::Working => write!(f, "working"), - Status::Pausing => write!(f, "pausing"), + Status::Working => write!(f, "pomodoro work"), + Status::Pausing => write!(f, "pomodoro pause"), + Status::LongPause => write!(f, "pomodoro long pause"), } } } @@ -65,7 +67,6 @@ impl Pomodoro { pub fn handle_expiration(&mut self, notify: bool) { self.timer.handle_expiration(notify); let duration = match self.status { - Status::Pausing => self.work, Status::Working => { if self.pauses == self.pauses_till_long { self.long_pause @@ -73,13 +74,14 @@ impl Pomodoro { self.pause } } + _ => self.work, }; self.status = match self.status { Status::Working => { self.pauses += 1; Status::Pausing } - Status::Pausing => Status::Working, + _ => Status::Working, }; self.timer = Timer::new(self.status.to_string().into_boxed_str(), duration); } diff --git a/src/timer.rs b/src/timer.rs index 9c59d53..8e4a953 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -44,9 +44,9 @@ impl Display for Timer { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, - "{} has {}s remaining.", + "{} has {} remaining.", self.name, - self.remaining().as_secs() + humantime::format_duration(self.remaining()) ) } } @@ -64,8 +64,10 @@ impl Timer { Instant::now() - self.start > self.duration } + /// Returns the remaining duration rounded to seconds of this [`Timer`]. pub fn remaining(&self) -> Duration { - self.duration - (Instant::now() - self.start) + let exact = self.duration - (Instant::now() - self.start); + Duration::from_secs(exact.as_secs()) } pub fn handle_expiration(&self, notify: bool) {