use clap::{Arg, Command};
use color_eyre::Result;
use color_eyre::eyre::eyre;
use std::time::Duration;
pub struct SleepOptions {
pub total_duration: Duration,
}
fn parse_time_interval(s: &str) -> Result<f64> {
if s.is_empty() {
return Err(eyre!("sleep: invalid time interval '{}'", s));
}
let (number_str, suffix) = if let Some(last_char) = s.chars().last() {
match last_char {
's' | 'm' | 'h' | 'd' => {
let num_str = &s[..s.len() - 1];
(num_str, Some(last_char))
}
_ => (s, None),
}
} else {
(s, None)
};
let number = number_str.parse::<f64>()
.map_err(|e| eyre!("sleep: invalid time interval '{}': {}", s, e))?;
let seconds = match suffix {
Some('s') | None => number,
Some('m') => number * 60.0,
Some('h') => number * 3600.0,
Some('d') => number * 86400.0,
_ => unreachable!(),
};
Ok(seconds)
}
pub fn parse_options(matches: &clap::ArgMatches) -> Result<SleepOptions> {
let intervals: Vec<String> = matches.get_many::<String>("number")
.map(|vals| vals.cloned().collect())
.unwrap_or_default();
if intervals.is_empty() {
return Err(eyre!("sleep: missing operand"));
}
let mut total_seconds = 0.0;
for interval in &intervals {
let seconds = parse_time_interval(interval)?;
total_seconds += seconds;
}
let total_duration = Duration::from_secs_f64(total_seconds);
Ok(SleepOptions { total_duration })
}
pub fn command() -> Command {
Command::new("sleep")
.about("Pause for NUMBER[SUFFIX]...")
.long_about("Pause for NUMBER seconds, where NUMBER is an integer or floating-point.\n\
SUFFIX may be 's','m','h', or 'd', for seconds, minutes, hours, days.\n\
With multiple arguments, pause for the sum of their values.")
.arg_required_else_help(true)
.arg(Arg::new("number")
.required(true)
.num_args(1..)
.help("Number of seconds to sleep (with optional suffix: s, m, h, d)"))
}
pub fn run(options: SleepOptions) -> Result<()> {
std::thread::sleep(options.total_duration);
Ok(())
}