feat: better durations and allow stopping pomodoro

This commit is contained in:
Moritz Böhme 2023-07-29 12:52:50 +02:00
parent 103c6d779e
commit 0c954962cc
Signed by: moritz
GPG key ID: 970C6E89EB0547A9
7 changed files with 81 additions and 49 deletions

7
Cargo.lock generated
View file

@ -562,6 +562,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.0.0"
@ -1117,6 +1123,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"humantime",
"notify-rust", "notify-rust",
"serde", "serde",
"serde_cbor", "serde_cbor",

View file

@ -8,6 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.71" anyhow = "1.0.71"
clap = { version = "4.3.4", features = ["derive"] } clap = { version = "4.3.4", features = ["derive"] }
humantime = "2.1.0"
notify-rust = "4.8.0" notify-rust = "4.8.0"
serde = { version = "1.0.164", features = ["derive"] } serde = { version = "1.0.164", features = ["derive"] }
serde_cbor = "0.11.2" serde_cbor = "0.11.2"

View file

@ -3,6 +3,7 @@ use anyhow::{Context, Result};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::net::Shutdown; use std::net::Shutdown;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::time::Duration;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[command(name = "timers")] #[command(name = "timers")]
@ -24,22 +25,29 @@ pub enum Command {
}, },
Add { Add {
name: String, name: String,
duration_seconds: u64, duration: humantime::Duration,
}, },
List, List,
Remove { Remove {
name: String, name: String,
}, },
Pomodoro { #[command(subcommand)]
#[clap(default_value_t = 25)] Pomodoro(PomodoroCommand)
work_minutes: u64, }
#[clap(default_value_t = 5)]
pause_minutes: u64, #[derive(Debug, Subcommand)]
#[clap(default_value_t = 10)] pub enum PomodoroCommand {
long_pause_minutes: u64, 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)] #[clap(default_value_t = 3)]
pauses_till_long: u64, pauses_till_long: u64,
} },
Stop,
} }
fn get_stream(socket_path: &String) -> Result<UnixStream> { fn get_stream(socket_path: &String) -> Result<UnixStream> {

View file

@ -16,12 +16,13 @@ pub enum Command {
Add(Box<str>, Duration), Add(Box<str>, Duration),
Remove(Box<str>), Remove(Box<str>),
List, List,
Pomodoro { PomodoroStart {
work: Duration, work: Duration,
pause: Duration, pause: Duration,
long_pause: Duration, long_pause: Duration,
pauses_till_long: u64, pauses_till_long: u64,
}, },
PomodoroStop
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -100,7 +101,14 @@ impl Daemon {
if self.notify { if self.notify {
match Notification::new() match Notification::new()
.summary("󰀠 Timers") .summary("󰀠 Timers")
.body(format!("Started timer {} for {:?}", &name, duration).as_str()) .body(
format!(
"Started timer {} for {}",
&name,
humantime::format_duration(duration)
)
.as_str(),
)
.show() .show()
{ {
Ok(_) => println!("Sent notification sucessfully."), Ok(_) => println!("Sent notification sucessfully."),
@ -120,23 +128,27 @@ impl Daemon {
.retain(|other| other.name.as_ref() != name.as_ref()); .retain(|other| other.name.as_ref() != name.as_ref());
Ok(Answer::Ok) Ok(Answer::Ok)
} }
Command::Pomodoro { Command::PomodoroStart {
work, work,
pause, pause,
long_pause, long_pause,
pauses_till_long, pauses_till_long,
} => { } => {
match Notification::new() match Notification::new()
.summary("󰀠 Timers") .summary("󰀠 Timers")
.body("Started pomodoro.") .body("Started pomodoro.")
.show() .show()
{ {
Ok(_) => println!("Sent notification sucessfully."), Ok(_) => println!("Sent notification sucessfully."),
Err(_) => println!("Failed to send notification."), Err(_) => println!("Failed to send notification."),
}; };
self.pomodoro = Some(Pomodoro::new(work, pause, long_pause, pauses_till_long)); self.pomodoro = Some(Pomodoro::new(work, pause, long_pause, pauses_till_long));
Ok(Answer::Ok) Ok(Answer::Ok)
} }
Command::PomodoroStop => {
self.pomodoro = None;
Ok(Answer::Ok)
},
} }
} }

View file

@ -3,34 +3,34 @@ pub mod daemon;
pub mod pomodoro; pub mod pomodoro;
pub mod timer; pub mod timer;
use std::time::Duration;
use crate::cli::{send_command, Cli, Command as CliCommand}; use crate::cli::{send_command, Cli, Command as CliCommand};
use crate::daemon::{Command as DaemonCommand, Daemon}; use crate::daemon::{Command as DaemonCommand, Daemon};
use anyhow::Result; use anyhow::Result;
use clap::Parser; use clap::Parser;
use cli::PomodoroCommand;
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Cli::parse(); let args = Cli::parse();
let daemon_command = match args.command { let daemon_command = match args.command {
CliCommand::Daemon { notify } => return Daemon::new(args.socket, notify)?.run(), CliCommand::Daemon { notify } => return Daemon::new(args.socket, notify)?.run(),
CliCommand::Add { CliCommand::Add { name, duration } => {
name, DaemonCommand::Add(name.into_boxed_str(), duration.into())
duration_seconds, }
} => DaemonCommand::Add(name.into_boxed_str(), Duration::from_secs(duration_seconds)),
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::Pomodoro { CliCommand::Pomodoro(pomodoro) => match pomodoro {
work_minutes, PomodoroCommand::Start {
pause_minutes, work,
long_pause_minutes, pause,
pauses_till_long, long_pause,
pauses_till_long,
} => DaemonCommand::Pomodoro{ } => DaemonCommand::PomodoroStart {
work: Duration::from_secs(work_minutes * 60), work: work.into(),
pause: Duration::from_secs(pause_minutes * 60), pause: pause.into(),
long_pause: Duration::from_secs(long_pause_minutes * 60), long_pause: long_pause.into(),
pauses_till_long pauses_till_long,
},
PomodoroCommand::Stop => DaemonCommand::PomodoroStop,
}, },
}; };
send_command(&args.socket, daemon_command) send_command(&args.socket, daemon_command)

View file

@ -19,12 +19,12 @@ impl Display for Pomodoro {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
"Pomodoro ({:?}, {:?}, {:?}) currently {} with {:?} remaining.", "Pomodoro ({}, {}, {}) currently {} with {} remaining.",
self.work, humantime::format_duration(self.work),
self.pause, humantime::format_duration(self.pause),
self.long_pause, humantime::format_duration(self.long_pause),
self.status, self.status,
self.timer.remaining() humantime::format_duration(self.timer.remaining())
) )
} }
} }
@ -33,13 +33,15 @@ impl Display for Pomodoro {
enum Status { enum Status {
Working, Working,
Pausing, Pausing,
LongPause,
} }
impl Display for Status { impl Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Status::Working => write!(f, "working"), Status::Working => write!(f, "pomodoro work"),
Status::Pausing => write!(f, "pausing"), 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) { pub fn handle_expiration(&mut self, notify: bool) {
self.timer.handle_expiration(notify); self.timer.handle_expiration(notify);
let duration = match self.status { let duration = match self.status {
Status::Pausing => self.work,
Status::Working => { Status::Working => {
if self.pauses == self.pauses_till_long { if self.pauses == self.pauses_till_long {
self.long_pause self.long_pause
@ -73,13 +74,14 @@ impl Pomodoro {
self.pause self.pause
} }
} }
_ => self.work,
}; };
self.status = match self.status { self.status = match self.status {
Status::Working => { Status::Working => {
self.pauses += 1; self.pauses += 1;
Status::Pausing Status::Pausing
} }
Status::Pausing => Status::Working, _ => Status::Working,
}; };
self.timer = Timer::new(self.status.to_string().into_boxed_str(), duration); self.timer = Timer::new(self.status.to_string().into_boxed_str(), duration);
} }

View file

@ -44,9 +44,9 @@ impl Display for Timer {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!( write!(
f, f,
"{} has {}s remaining.", "{} has {} remaining.",
self.name, self.name,
self.remaining().as_secs() humantime::format_duration(self.remaining())
) )
} }
} }
@ -64,8 +64,10 @@ impl Timer {
Instant::now() - self.start > self.duration Instant::now() - self.start > self.duration
} }
/// Returns the remaining duration rounded to seconds of this [`Timer`].
pub fn remaining(&self) -> Duration { 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) { pub fn handle_expiration(&self, notify: bool) {