Args (#14)
This commit is contained in:
20
src/app.rs
20
src/app.rs
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
args::Args,
|
||||
constants::TICK_VALUE_MS,
|
||||
events::{Event, EventHandler, Events},
|
||||
terminal::Terminal,
|
||||
@@ -7,7 +8,7 @@ use crate::{
|
||||
countdown::{Countdown, CountdownWidget},
|
||||
footer::Footer,
|
||||
header::Header,
|
||||
pomodoro::{Pomodoro, PomodoroWidget},
|
||||
pomodoro::{Pomodoro, PomodoroArgs, PomodoroWidget},
|
||||
timer::{Timer, TimerWidget},
|
||||
},
|
||||
};
|
||||
@@ -44,29 +45,26 @@ pub struct App {
|
||||
pomodoro: Pomodoro,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
impl App {
|
||||
pub fn new(args: Args) -> Self {
|
||||
Self {
|
||||
mode: Mode::Running,
|
||||
content: Content::Countdown,
|
||||
show_menu: false,
|
||||
countdown: Countdown::new(Clock::<clock::Countdown>::new(
|
||||
Duration::from_secs(10 * 60 /* 10min */),
|
||||
args.countdown,
|
||||
Duration::from_millis(TICK_VALUE_MS),
|
||||
)),
|
||||
timer: Timer::new(Clock::<clock::Timer>::new(
|
||||
Duration::ZERO,
|
||||
Duration::from_millis(TICK_VALUE_MS),
|
||||
)),
|
||||
pomodoro: Pomodoro::new(),
|
||||
pomodoro: Pomodoro::new(PomodoroArgs {
|
||||
work: args.work,
|
||||
pause: args.pause,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub async fn run(&mut self, mut terminal: Terminal, mut events: Events) -> Result<()> {
|
||||
while self.is_running() {
|
||||
|
||||
95
src/args.rs
Normal file
95
src/args.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use clap::Parser;
|
||||
use color_eyre::{
|
||||
eyre::{ensure, eyre},
|
||||
Report,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Args {
|
||||
#[arg(long, short, value_parser = parse_duration,
|
||||
default_value="10:00" /* 10min */,
|
||||
help = "Countdown time to start from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub countdown: Duration,
|
||||
#[arg(long, short, value_parser = parse_duration,
|
||||
default_value="25:00" /* 25min */,
|
||||
help = "Work time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub work: Duration,
|
||||
#[arg(long, short, value_parser = parse_duration,
|
||||
default_value="5:00" /* 5min */,
|
||||
help = "Pause time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub pause: Duration,
|
||||
}
|
||||
|
||||
fn parse_duration(arg: &str) -> Result<Duration, Report> {
|
||||
let parts: Vec<&str> = arg.split(':').rev().collect();
|
||||
|
||||
let parse_seconds = |s: &str| -> Result<u64, Report> {
|
||||
let secs = s.parse::<u64>().map_err(|_| eyre!("Invalid seconds"))?;
|
||||
ensure!(secs < 60, "Seconds must be less than 60.");
|
||||
Ok(secs)
|
||||
};
|
||||
|
||||
let parse_minutes = |m: &str| -> Result<u64, Report> {
|
||||
let mins = m.parse::<u64>().map_err(|_| eyre!("Invalid minutes"))?;
|
||||
ensure!(mins < 60, "Minutes must be less than 60.");
|
||||
Ok(mins)
|
||||
};
|
||||
|
||||
let parse_hours = |h: &str| -> Result<u64, Report> {
|
||||
let hours = h.parse::<u64>().map_err(|_| eyre!("Invalid hours"))?;
|
||||
ensure!(hours < 100, "Hours must be less than 100.");
|
||||
Ok(hours)
|
||||
};
|
||||
|
||||
let seconds = match parts.as_slice() {
|
||||
[ss] => parse_seconds(ss)?,
|
||||
[ss, mm] => {
|
||||
let s = parse_seconds(ss)?;
|
||||
let m = parse_minutes(mm)?;
|
||||
m * 60 + s
|
||||
}
|
||||
[ss, mm, hh] => {
|
||||
let s = parse_seconds(ss)?;
|
||||
let m = parse_minutes(mm)?;
|
||||
let h = parse_hours(hh)?;
|
||||
h * 60 * 60 + m * 60 + s
|
||||
}
|
||||
_ => return Err(eyre!("Invalid time format. Use 'ss', mm:ss, or hh:mm:ss")),
|
||||
};
|
||||
|
||||
Ok(Duration::from_secs(seconds))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_duration() {
|
||||
// ss
|
||||
assert_eq!(parse_duration("50").unwrap(), Duration::from_secs(50));
|
||||
|
||||
// mm:ss
|
||||
assert_eq!(
|
||||
parse_duration("01:30").unwrap(),
|
||||
Duration::from_secs(60 + 30)
|
||||
);
|
||||
|
||||
// hh:mm:ss
|
||||
assert_eq!(
|
||||
parse_duration("01:30:00").unwrap(),
|
||||
Duration::from_secs(60 * 60 + 30 * 60)
|
||||
);
|
||||
|
||||
// errors
|
||||
assert!(parse_duration("1:60").is_err()); // invalid seconds
|
||||
assert!(parse_duration("60:00").is_err()); // invalid minutes
|
||||
assert!(parse_duration("100:00:00").is_err()); // invalid hours
|
||||
assert!(parse_duration("abc").is_err()); // invalid input
|
||||
assert!(parse_duration("01:02:03:04").is_err()); // too many parts
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,14 @@ mod events;
|
||||
#[cfg(debug_assertions)]
|
||||
mod logging;
|
||||
|
||||
mod args;
|
||||
mod terminal;
|
||||
mod utils;
|
||||
mod widgets;
|
||||
|
||||
use app::App;
|
||||
use args::Args;
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
|
||||
#[tokio::main]
|
||||
@@ -20,9 +23,11 @@ async fn main() -> Result<()> {
|
||||
|
||||
color_eyre::install()?;
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let terminal = terminal::setup()?;
|
||||
let events = events::Events::new();
|
||||
App::new().run(terminal, events).await?;
|
||||
App::new(args).run(terminal, events).await?;
|
||||
terminal::teardown()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -15,9 +15,6 @@ use std::{cmp::max, time::Duration};
|
||||
|
||||
use strum::Display;
|
||||
|
||||
static PAUSE_MS: u64 = 5 * 60 * 1000; /* 5min in milliseconds */
|
||||
static WORK_MS: u64 = 25 * 60 * 1000; /* 25min in milliseconds */
|
||||
|
||||
#[derive(Debug, Clone, Display, Hash, Eq, PartialEq)]
|
||||
enum Mode {
|
||||
Work,
|
||||
@@ -45,19 +42,18 @@ pub struct Pomodoro {
|
||||
clock_map: ClockMap,
|
||||
}
|
||||
|
||||
pub struct PomodoroArgs {
|
||||
pub work: Duration,
|
||||
pub pause: Duration,
|
||||
}
|
||||
|
||||
impl Pomodoro {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(args: PomodoroArgs) -> Self {
|
||||
Self {
|
||||
mode: Mode::Work,
|
||||
clock_map: ClockMap {
|
||||
work: Clock::<Countdown>::new(
|
||||
Duration::from_millis(WORK_MS),
|
||||
Duration::from_millis(TICK_VALUE_MS),
|
||||
),
|
||||
pause: Clock::<Countdown>::new(
|
||||
Duration::from_millis(PAUSE_MS),
|
||||
Duration::from_millis(TICK_VALUE_MS),
|
||||
),
|
||||
work: Clock::<Countdown>::new(args.work, Duration::from_millis(TICK_VALUE_MS)),
|
||||
pause: Clock::<Countdown>::new(args.pause, Duration::from_millis(TICK_VALUE_MS)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -91,7 +87,7 @@ impl EventHandler for Pomodoro {
|
||||
KeyCode::Left if edit_mode => {
|
||||
self.get_clock().edit_next();
|
||||
}
|
||||
KeyCode::Left if edit_mode => {
|
||||
KeyCode::Left => {
|
||||
// `next` is acting as same as a `prev` function, we don't have
|
||||
self.next();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user