feat: add proper parsing of cli args with corresponding error type
Diff
src/error.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/main.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 140 insertions(+), 12 deletions(-)
@@ -0,0 +1,75 @@
use std::fmt::Display;
use humantime::DurationError;
#[derive(Debug)]
pub struct ParseError {
pub message: String,
}
impl ParseError {
pub fn new(message: &str) -> ParseError {
ParseError {
message: message.into(),
}
}
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for ParseError {}
impl From<DurationError> for ParseError {
fn from(value: DurationError) -> Self {
ParseError::new(&value.to_string())
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
#[test]
fn test_parse_error_new() {
let error_message = "test error";
let error = ParseError::new(error_message);
assert_eq!(error.message, error_message);
}
#[test]
fn test_parse_duration() {
let good_duration = "35s";
let bad_duration = "35mi";
let first_test = humantime::parse_duration(good_duration).unwrap();
assert_eq!(first_test, Duration::new(35, 0));
let second_test = humantime::parse_duration(bad_duration);
assert!(second_test.is_err());
}
#[test]
fn test_parse_error_display() {
let error_message = "display error";
let error = ParseError::new(error_message);
assert_eq!(format!("{}", error), error_message);
}
}
@@ -1,15 +1,35 @@
use std::path::PathBuf;
use clap::Parser;
use humantime::Duration;
use tokio::time::sleep;
#[derive(Debug, Parser, Default)]
use crate::error::ParseError;
mod error;
#[derive(Debug, Parser)]
struct CliArgs {
#[clap(short, long, default_value = "10m")]
pub refresh_time: String,
#[arg(value_parser = parse_refresh_time)]
pub refresh_time: Duration,
#[clap(required = true)]
pub directory: String,
#[arg(value_parser = parse_directory)]
pub directory: PathBuf,
}
impl Default for CliArgs {
fn default() -> Self {
Self {
refresh_time: parse_refresh_time("10m").unwrap(),
directory: Default::default(),
}
}
}
#[tokio::main]
@@ -21,13 +41,46 @@ async fn main() {
async fn do_sync_task(args: CliArgs) {
println!("I am awake! {}", args.directory);
sleep(
args.refresh_time
.parse::<humantime::Duration>()
.expect("Unable to convert refresh_time")
.into(),
)
.await;
println!("I am awake! {:?}", args);
sleep(args.refresh_time.into()).await;
}
fn parse_directory(arg: &str) -> Result<PathBuf, ParseError> {
let directory = PathBuf::from(arg);
if directory.exists() && directory.is_dir() {
Ok(PathBuf::from(arg))
} else {
Err(ParseError::new(
"path does not exist or is not a directory.",
))
}
}
fn parse_refresh_time(arg: &str) -> Result<humantime::Duration, ParseError> {
arg.parse::<humantime::Duration>().map_err(ParseError::from)
}