use errgonomic::{handle, handle_opt};
use std::num::ParseIntError;
use thiserror::Error;
use time::Duration;
pub fn parse_duration_value(source: &impl AsRef<str>) -> Result<Option<Duration>, DurationValueParserError> {
use DurationValueParserError::*;
let trimmed = source.as_ref().trim();
if trimmed.is_empty() {
return Ok(None);
}
let mut tokens = trimmed.split_whitespace();
let number_str = handle_opt!(tokens.next(), NumberNotFound);
let unit_str = handle_opt!(tokens.next(), UnitNotFound);
let number = handle!(number_str.parse::<i64>(), NumberParseFailed);
let duration = match unit_str.to_ascii_lowercase().as_str() {
"second" | "seconds" => Duration::seconds(number),
"minute" | "minutes" => Duration::minutes(number),
"hour" | "hours" => Duration::hours(number),
"day" | "days" => Duration::days(number),
_ => {
return Err(UnitUnexpected {
unit: unit_str.to_owned(),
});
}
};
Ok(Some(duration))
}
#[derive(Debug, Error)]
pub enum DurationValueParserError {
#[error("duration value does not contain a number")]
NumberNotFound,
#[error("duration value does not contain a unit")]
UnitNotFound,
#[error("failed to parse duration number: {source}")]
NumberParseFailed {
#[source]
source: ParseIntError,
},
#[error("unexpected duration unit: {unit}")]
UnitUnexpected { unit: String },
}