Compare commits

..

3 Commits

Author SHA1 Message Date
Moritz Böhme 56beda6840
fix: pauses not resetting 2023-07-29 16:11:25 +02:00
Moritz Böhme 61b7bd447d
feat: add command to list pomodoro 2023-07-29 16:02:42 +02:00
Moritz Böhme 98acf3c74e
feat: add documentation 2023-07-29 15:59:33 +02:00
4 changed files with 66 additions and 27 deletions

View File

@ -5,10 +5,9 @@ use std::net::Shutdown;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::time::Duration; use std::time::Duration;
#[derive(Debug, Parser)] #[derive(Parser)]
#[command(name = "timers")] #[command(name = "timers")]
#[command(about = "A advanced timer daemon/cli.", long_about = None)] /// A advanced timer daemon/cli.
#[command(arg_required_else_help = true)]
pub struct Cli { pub struct Cli {
#[command(subcommand)] #[command(subcommand)]
pub command: Command, pub command: Command,
@ -19,35 +18,67 @@ pub struct Cli {
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
pub enum Command { pub enum Command {
/// Run as daemon
#[clap(visible_alias="d")]
Daemon { Daemon {
/// do not send notifications
#[arg(short, long)] #[arg(short, long)]
notify: bool, no_notify: bool,
}, },
/// Add a timer
#[clap(visible_alias="a")]
Add { Add {
/// name of the timer
name: String, name: String,
/// duration of the timer
duration: humantime::Duration, duration: humantime::Duration,
}, },
/// List timers
#[clap(visible_alias="l")]
List, List,
/// Remove a timer
#[clap(visible_alias="r")]
Remove { Remove {
/// name of the timer to remove
name: String, name: String,
}, },
/// Pomodoro specific command
#[command(subcommand)] #[command(subcommand)]
#[clap(visible_alias="p")]
Pomodoro(PomodoroCommand) Pomodoro(PomodoroCommand)
} }
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
pub enum PomodoroCommand { pub enum PomodoroCommand {
/// Start pomodoro
#[clap(visible_alias="s")]
Start { Start {
/// duration to work for
#[arg(long)]
#[clap(default_value_t = Duration::from_secs(25 * 60).into())] #[clap(default_value_t = Duration::from_secs(25 * 60).into())]
work: humantime::Duration, work: humantime::Duration,
/// duration for short pauses
#[arg(long)]
#[clap(default_value_t = Duration::from_secs(5 * 60).into())] #[clap(default_value_t = Duration::from_secs(5 * 60).into())]
pause: humantime::Duration, pause: humantime::Duration,
/// duration for long pauses
#[arg(long)]
#[clap(default_value_t = Duration::from_secs(10 * 60).into())] #[clap(default_value_t = Duration::from_secs(10 * 60).into())]
long_pause: humantime::Duration, long_pause: humantime::Duration,
/// number of short pauses till long pause
#[arg(long)]
#[clap(default_value_t = 3)] #[clap(default_value_t = 3)]
pauses_till_long: u64, pauses_till_long: u64,
}, },
Stop, /// Stop the pomodoro
#[clap(visible_alias="p")]
Remove,
/// List the pomodoro settings and remaining duration
#[clap(visible_alias="l")]
List,
} }
fn get_stream(socket_path: &String) -> Result<UnixStream> { fn get_stream(socket_path: &String) -> Result<UnixStream> {

View File

@ -22,33 +22,34 @@ pub enum Command {
long_pause: Duration, long_pause: Duration,
pauses_till_long: u64, pauses_till_long: u64,
}, },
PomodoroStop PomodoroRemove,
PomodoroList,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum Answer { pub enum Answer {
Ok, Ok,
Timers(Vec<Timer>, Option<Pomodoro>), Timers(Vec<Timer>),
Pomodoro(Option<Pomodoro>),
} }
impl Display for Answer { impl Display for Answer {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self { match self {
Answer::Ok => write!(f, "Ok"), Answer::Ok => write!(f, "Ok"),
Answer::Timers(timers, pomodoro) => { Answer::Timers(timers) => {
if timers.is_empty() { if timers.is_empty() {
writeln!(f, "No timers running.")?; writeln!(f, "No timers running.")
} else { } else {
let strings: Vec<String> = let strings: Vec<String> =
timers.iter().map(|timer| timer.to_string()).collect(); timers.iter().map(|timer| timer.to_string()).collect();
writeln!(f, "{}", strings.join("\n"))?; writeln!(f, "{}", strings.join("\n"))
}; }
}
match pomodoro { Answer::Pomodoro(pomodoro) => match pomodoro {
Some(p) => write!(f, "{}", p), Some(p) => write!(f, "{}", p),
None => write!(f, "No pomodoro running."), None => write!(f, "No pomodoro running."),
} },
}
} }
} }
} }
@ -69,7 +70,7 @@ pub struct Daemon {
} }
impl Daemon { impl Daemon {
pub fn new(socket_path: String, notify: bool) -> anyhow::Result<Self> { pub fn new(socket_path: String, no_notify: bool) -> anyhow::Result<Self> {
let path = std::path::Path::new(&socket_path); let path = std::path::Path::new(&socket_path);
if path.exists() { if path.exists() {
std::fs::remove_file(path) std::fs::remove_file(path)
@ -81,7 +82,7 @@ impl Daemon {
listener, listener,
timers: Vec::new(), timers: Vec::new(),
pomodoro: None, pomodoro: None,
notify, notify: !no_notify,
}) })
} }
@ -92,7 +93,7 @@ impl Daemon {
fn handle_command(&mut self, command: Command) -> Result<Answer, AnswerErr> { fn handle_command(&mut self, command: Command) -> Result<Answer, AnswerErr> {
println!("Received command {:?}", command); println!("Received command {:?}", command);
match command { match command {
Command::List => Ok(Answer::Timers(self.timers.clone(), self.pomodoro.clone())), Command::List => Ok(Answer::Timers(self.timers.clone())),
Command::Add(name, duration) => { Command::Add(name, duration) => {
if self.has_timer(&name) { if self.has_timer(&name) {
return Err(AnswerErr::TimerAlreadyExist(name)); return Err(AnswerErr::TimerAlreadyExist(name));
@ -145,10 +146,11 @@ impl Daemon {
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 => { Command::PomodoroRemove => {
self.pomodoro = None; self.pomodoro = None;
Ok(Answer::Ok) Ok(Answer::Ok)
}, }
Command::PomodoroList => Ok(Answer::Pomodoro(self.pomodoro.clone())),
} }
} }

View File

@ -12,7 +12,7 @@ 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 { no_notify } => return Daemon::new(args.socket, no_notify)?.run(),
CliCommand::Add { name, duration } => { CliCommand::Add { name, duration } => {
DaemonCommand::Add(name.into_boxed_str(), duration.into()) DaemonCommand::Add(name.into_boxed_str(), duration.into())
} }
@ -30,7 +30,8 @@ fn main() -> Result<()> {
long_pause: long_pause.into(), long_pause: long_pause.into(),
pauses_till_long, pauses_till_long,
}, },
PomodoroCommand::Stop => DaemonCommand::PomodoroStop, PomodoroCommand::Remove => DaemonCommand::PomodoroRemove,
PomodoroCommand::List => DaemonCommand::PomodoroList,
}, },
}; };
send_command(&args.socket, daemon_command) send_command(&args.socket, daemon_command)

View File

@ -39,9 +39,9 @@ enum Status {
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, "pomodoro work"), Status::Working => write!(f, "work"),
Status::Pausing => write!(f, "pomodoro pause"), Status::Pausing => write!(f, "pause"),
Status::LongPause => write!(f, "pomodoro long pause"), Status::LongPause => write!(f, "long pause"),
} }
} }
} }
@ -78,9 +78,14 @@ impl Pomodoro {
}; };
self.status = match self.status { self.status = match self.status {
Status::Working => { Status::Working => {
if self.pauses == self.pauses_till_long {
self.pauses = 0;
Status::LongPause
} else {
self.pauses += 1; self.pauses += 1;
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);