Skip to content

Commit

Permalink
Prevent using conflicting command arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
jkallio committed Jan 20, 2024
1 parent 099ee7a commit 6894a4f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 35 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,28 @@ $ cargo install pomodoro-cli

Options for `start`:
- `--duration` Set the duration for the timer (format: `1h 30m 15s`)
- `--add` Add more time to a running timer instead of starting a new timer (format: `1h 30m 15s`)
- `--resume` Resume a paused timer (default: disabled)
- `--notify` Triggers system notification when the timer is finished (default: disabled)
- `--silent` Do not play alarm sound when the timer is finished (default: enabled)
- `--wait` Wait for the timer to finish (default: disabled)
- `--add` Add more time to a running timer instead of starting a new timer (default: disabled)
- `--resume` Resume a paused timer (default: disabled)

### Start/Stop the timer

```bash
# Start the timer with default configuration
# Start the timer with default configuration (25 min with alarm sound)
$ pomodoro-cli start

# Start the timer with custom configuration
$ pomodoro-cli start --duration "1h 30m 15s" --silent --notify
# Start a 30 min timer wihout playing alarm sound, but triggering a system notification
$ pomodoro-cli start --duration "30m" --silent --notify

# Stop the timer
$ pomodoro-cli stop
```

### Pause/Resume the timer

```bash
# Pause the Timer (calling this command again will resume the timer)
$ pomodoro-cli pause

Expand All @@ -69,7 +73,7 @@ $ pomodoro-cli start --resume

```bash
# Add 10 minutes to the timer (instead of starting a new timer)
$ pomodoro-cli start -d 10 --add
$ pomodoro-cli start -add 10m
```

### Query the timer status
Expand All @@ -96,7 +100,7 @@ Add the following module to your waybar configuration:
"format": "  {}",
"exec": "pomodoro-cli status --format json",
"return-type": "json",
"on-click": "pomodoro-cli start --duration 5m --add --notify",
"on-click": "pomodoro-cli start --add 5m --notify",
"on-click-middle": "pomodoro-cli pause",
"on-click-right": "pomodoro-cli stop",
"interval": 1
Expand Down Expand Up @@ -127,7 +131,7 @@ If you want to signal Waybar to update the module immediately when you can add `

```json
"custom/pomo": {
"on-click": "pomodoro-cli start --duration 5m; pkill -SIGRTMIN+10 waybar",
"on-click": "pomodoro-cli start --add 5m; pkill -SIGRTMIN+10 waybar",
"signal": 10,
}
```
Expand Down
19 changes: 12 additions & 7 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::args::*;
use crate::error::*;
use crate::timer_info::DEFAULT_TIMER_DURATION;
use crate::timer_info::{TimerInfo, TimerState};
use crate::utils::*;
use crossterm::cursor::{Hide, MoveToColumn, MoveToPreviousLine, Show};
Expand All @@ -22,9 +23,9 @@ pub fn run(args: &Cli) -> AppResult<()> {
} => {
start_timer(
parse_duration(duration.clone()),
parse_duration(add.clone()),
*silent,
*notify,
*add,
*resume,
)?;
if *wait {
Expand All @@ -47,16 +48,18 @@ pub fn run(args: &Cli) -> AppResult<()> {

/// Start the timer. If the timer is already running, the duration is added to the current duration.
pub fn start_timer(
duration: i64,
duration: Option<i64>,
add: Option<i64>,
silent: bool,
notify: bool,
add: bool,
resume: bool,
) -> AppResult<()> {
let mut timer_info = TimerInfo::from_file_or_default()?;
if timer_info.is_running() && add {
timer_info.duration += duration;
if timer_info.is_running() && add.is_some() {
// Add more time to the timer
timer_info.duration += add.unwrap();
} else if timer_info.is_paused() && resume {
// Resume a paused timer
let now = chrono::Utc::now().timestamp();
let elapsed = timer_info.pause_time - timer_info.start_time;
timer_info.duration = timer_info.duration - elapsed;
Expand All @@ -66,6 +69,8 @@ pub fn start_timer(
timer_info.notify = timer_info.notify || notify;
timer_info.state = TimerState::Running;
} else {
// Start a new timer
let duration = duration.unwrap_or(add.unwrap_or(DEFAULT_TIMER_DURATION));
let now = chrono::Utc::now().timestamp() + 1;
timer_info.duration = duration;
timer_info.start_time = now;
Expand All @@ -88,11 +93,11 @@ pub fn pause_timer() -> AppResult<()> {
timer_info.write_to_file()?;
} else {
start_timer(
timer_info.duration,
Some(timer_info.duration),
None,
timer_info.silent,
timer_info.notify,
false,
true,
)?;
}
Ok(())
Expand Down
18 changes: 14 additions & 4 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,22 @@ pub struct Cli {
pub enum SubCommand {
/// Start a new timer
Start {
#[arg(short, long, help = "Duration of the timer in format 1h 30m 20s")]
#[arg(
short,
long,
conflicts_with_all = &["add", "resume"],
help = "Duration of the timer in format 1h 30m 20s"
)]
duration: Option<String>,

#[arg(
short,
long,
conflicts_with_all = &["resume"],
help = "Add more time to the timer"
)]
add: Option<String>,

#[arg(long, default_value_t = false, help = "Enable system notification")]
notify: bool,

Expand All @@ -30,9 +43,6 @@ pub enum SubCommand {
#[arg(long, default_value_t = false, help = "Wait for the timer to finish")]
wait: bool,

#[arg(long, default_value_t = false, help = "Add more time to the timer")]
add: bool,

#[arg(long, default_value_t = false, help = "Resume paused timer")]
resume: bool,
},
Expand Down
37 changes: 21 additions & 16 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::timer_info::DEFAULT_TIMER_DURATION;
use std::path::PathBuf;

/// Return the path to the timer information file. This is the cache directory on Linux and
Expand Down Expand Up @@ -38,10 +37,10 @@ pub fn get_custom_icon_file() -> Option<PathBuf> {
}

/// The duration can be passed either as a number (as minutes) or as string in the format of "1h 30m 10s"
pub fn parse_duration(duration: Option<String>) -> i64 {
pub fn parse_duration(duration: Option<String>) -> Option<i64> {
if let Some(duration) = duration {
if let Ok(duration) = duration.parse::<i64>() {
return duration * 60;
return Some(duration * 60);
}

let mut duration = duration.to_lowercase();
Expand Down Expand Up @@ -72,9 +71,9 @@ pub fn parse_duration(duration: Option<String>) -> i64 {
let parts = duration.split("s").collect::<Vec<&str>>();
seconds = parts[0].parse().unwrap_or_default();
}
return hours * 60 * 60 + minutes * 60 + seconds;
return Some(hours * 60 * 60 + minutes * 60 + seconds);
}
DEFAULT_TIMER_DURATION
None
}

/// Return the seconds in human-readable format (e.g. 1h 30m 10s)
Expand All @@ -101,6 +100,9 @@ pub fn get_human_readable_time(seconds: i64) -> String {
}
time.push_str(&format!("{}s", seconds));
}
if time.is_empty() {
time.push_str("0s");
}
return time;
}

Expand All @@ -110,22 +112,25 @@ mod tests {

#[test]
fn test_parse_duration() {
assert_eq!(parse_duration(Some("1h 30m 10s".to_string())), 5410);
assert_eq!(parse_duration(Some("1H 30Min 10SeC".to_string())), 5410);
assert_eq!(parse_duration(Some("2h15m1s".to_string())), 8101);
assert_eq!(parse_duration(Some("1h 30m".to_string())), 5400);
assert_eq!(parse_duration(Some("1hour".to_string())), 3600);
assert_eq!(parse_duration(Some("30m 10s".to_string())), 1810);
assert_eq!(parse_duration(Some("30m".to_string())), 1800);
assert_eq!(parse_duration(Some("10s".to_string())), 10);
assert_eq!(parse_duration(Some("100".to_string())), 100 * 60);
assert_eq!(parse_duration(Some("Invalid string".to_string())), 0);
assert_eq!(parse_duration(Some("1h 30m 10s".to_string())), Some(5410));
assert_eq!(
parse_duration(Some("1H 30Min 10SeC".to_string())),
Some(5410)
);
assert_eq!(parse_duration(Some("2h15m1s".to_string())), Some(8101));
assert_eq!(parse_duration(Some("1h 30m".to_string())), Some(5400));
assert_eq!(parse_duration(Some("1hour".to_string())), Some(3600));
assert_eq!(parse_duration(Some("30m 10s".to_string())), Some(1810));
assert_eq!(parse_duration(Some("30m".to_string())), Some(1800));
assert_eq!(parse_duration(Some("10s".to_string())), Some(10));
assert_eq!(parse_duration(Some("100".to_string())), Some(100 * 60));
assert_eq!(parse_duration(Some("Invalid string".to_string())), Some(0));
}

#[test]
fn test_get_human_readable_time() {
assert_eq!(get_human_readable_time(5410), "1h 30m 10s");
assert_eq!(get_human_readable_time(60), "1m 0s");
assert_eq!(get_human_readable_time(60), "1m");
assert_eq!(get_human_readable_time(10), "10s");
assert_eq!(get_human_readable_time(0), "0s");
}
Expand Down

0 comments on commit 6894a4f

Please sign in to comment.