move parse_duration into duration module

This commit is contained in:
jk 2025-01-01 18:23:19 +01:00
parent a4f8885eb1
commit e4dcf61153
No known key found for this signature in database
2 changed files with 74 additions and 76 deletions

View File

@ -1,26 +1,24 @@
use clap::Parser;
use color_eyre::{
eyre::{ensure, eyre},
Report,
use crate::{
common::{Content, Style},
duration,
};
use clap::Parser;
use std::time::Duration;
use crate::common::{Content, Style};
#[derive(Parser)]
#[command(version)]
pub struct Args {
#[arg(long, short, value_parser = parse_duration,
#[arg(long, short, value_parser = duration::parse_duration,
help = "Countdown time to start from. Formats: 'ss', 'mm:ss', or 'hh:mm:ss'"
)]
pub countdown: Option<Duration>,
#[arg(long, short, value_parser = parse_duration,
#[arg(long, short, value_parser = duration::parse_duration,
help = "Work time to count down from. Formats: 'ss', 'mm:ss', or 'hh:mm:ss'"
)]
pub work: Option<Duration>,
#[arg(long, short, value_parser = parse_duration,
#[arg(long, short, value_parser = duration::parse_duration,
help = "Pause time to count down from. Formats: 'ss', 'mm:ss', or 'hh:mm:ss'"
)]
pub pause: Option<Duration>,
@ -40,70 +38,3 @@ pub struct Args {
#[arg(long, short = 'r', help = "Reset stored values to default.")]
pub reset: bool,
}
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
}
}

View File

@ -1,3 +1,7 @@
use color_eyre::{
eyre::{ensure, eyre},
Report,
};
use std::fmt;
use std::time::Duration;
@ -114,6 +118,47 @@ impl fmt::Display for DurationEx {
}
}
/// Parses `Duration` from `hh:mm:ss`, `mm:ss` or `ss`
pub 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 {
@ -157,4 +202,26 @@ mod tests {
let ex3 = ex.saturating_add(ex2);
assert_eq!(format!("{}", ex3), "11");
}
#[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
}
}