use crate::unit::{opt_unit_abbr, TimeUnit};
use crate::{Calc, CondUnit, ExpectErr};
use std::time::Duration;
use winnow::ascii::{digit1, multispace0};
use winnow::combinator::{alt, cut_err};
use winnow::combinator::{eof, peek, repeat};
use winnow::error::{ContextError, StrContext, StrContextValue};
use winnow::ModalResult as WResult;
use winnow::Parser;
pub(crate) fn cond_unit1(input: &mut &str) -> WResult<CondUnit> {
alt(('+'.value(CondUnit::Plus), '*'.value(CondUnit::Star)))
.context(StrContext::Expected(StrContextValue::Description(
CondUnit::get_expect_val(),
)))
.parse_next(input)
}
fn opt_cond_unit(input: &mut &str) -> WResult<CondUnit> {
let result = cond_unit1.parse_next(input);
if result.is_err() {
multispace0.parse_next(input)?;
if eof::<_, ContextError>.parse_next(input).is_ok() {
return Ok(CondUnit::Plus);
}
return cut_err(peek((digit1, multispace0, opt_unit_abbr)))
.context(StrContext::Expected(StrContextValue::Description(
CondUnit::get_expect_val(),
)))
.value(CondUnit::Plus)
.parse_next(input);
}
result
}
pub(crate) fn parse_expr_time(input: &mut &str) -> WResult<u64> {
(multispace0, digit1, multispace0, opt_unit_abbr, multispace0)
.map(|x| (x.1, x.3))
.try_map(|(v, unit)| unit.duration(v))
.parse_next(input)
}
pub(crate) fn cond_time<'a>(input: &mut &'a str) -> WResult<Vec<(&'a str, CondUnit, TimeUnit)>> {
repeat(
0..,
(
multispace0,
opt_cond_unit,
multispace0,
digit1,
multispace0,
opt_unit_abbr,
multispace0,
)
.map(|x| (x.3, x.1, x.5)),
)
.fold(Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
})
.parse_next(input)
}
pub fn parse(input: impl AsRef<str>) -> Result<Duration, String> {
let input = input.as_ref();
if input.is_empty() {
return Err(String::from("Empty input"));
}
#[cfg(feature = "no_calc")]
{
use crate::DError;
let d = repeat(0.., parse_expr_time)
.try_fold(
Default::default,
|mut acc: u64, item| -> Result<_, DError> {
acc = acc.checked_add(item).ok_or(DError::OverflowError)?;
Ok(acc)
},
)
.parse(input)
.map_err(|err| err.to_string())?;
Ok(Duration::from_nanos(d))
}
#[cfg(not(feature = "no_calc"))]
{
let (unit_time, cond_val) = (parse_expr_time, cond_time)
.parse(input)
.map_err(|e| format!("{}", e))?;
let (init_cond, init_duration) = if cond_val.is_empty() {
CondUnit::init()
} else {
cond_val.calc().map_err(|err| err.to_string())?
};
let duration = init_cond
.calc(unit_time, init_duration)
.map_err(|err| err.to_string())?;
Ok(duration)
}
}
#[cfg(test)]
#[allow(clippy::identity_op)]
mod tests {
use super::*;
use crate::{catch_err, unit::TimeUnit, CondUnit};
#[test]
fn test_parse_expr_time() {
let (input, val) = parse_expr_time.parse_peek("123m").unwrap();
assert_eq!(input, "");
assert_eq!(val, 7380000000000);
}
#[test]
fn test_cond_unit() {
let (input, format) = cond_unit1.parse_peek("*123").unwrap();
assert_eq!(input, "123");
assert_eq!(format, CondUnit::Star);
}
#[test]
fn test_cond_time() {
let (input, out) = cond_time.parse_peek(" * 60").unwrap();
assert_eq!(input, "");
assert_eq!(out, vec![("60", CondUnit::Star, TimeUnit::Second)]);
}
#[test]
fn test_cond_time2() {
let (input, out) = cond_time.parse_peek(" * 60*30").unwrap();
assert_eq!(input, "");
assert_eq!(
out,
vec![
("60", CondUnit::Star, TimeUnit::Second),
("30", CondUnit::Star, TimeUnit::Second),
]
);
}
#[test]
fn test_duration_parse0() {
let duration = parse("0").unwrap();
assert_eq!(duration, Duration::new(0, 0));
let duration = parse("0 ").unwrap();
assert_eq!(duration, Duration::new(0, 0));
let duration = parse(" 0 ").unwrap();
assert_eq!(duration, Duration::new(0, 0));
let duration = parse("1").unwrap();
assert_eq!(duration, Duration::new(1, 0));
let duration = parse("0m").unwrap();
assert_eq!(duration, Duration::new(0, 0));
let duration = parse("1hr").unwrap();
assert_eq!(duration, Duration::new(3600, 0));
let duration = parse("1m31").unwrap();
assert_eq!(duration, Duration::new(91, 0));
let duration = parse("1m31s").unwrap();
assert_eq!(duration, Duration::new(91, 0));
#[cfg(not(feature = "no_calc"))]
{
let duration = parse("1m+31").unwrap();
assert_eq!(duration, Duration::new(91, 0));
let duration = parse("1m*60").unwrap();
assert_eq!(duration, Duration::new(3600, 0));
let duration = parse("1m*60*20").unwrap();
assert_eq!(duration, Duration::new(72000, 0));
let duration = parse("1m+60+24").unwrap();
assert_eq!(duration, Duration::new(144, 0));
let duration = parse("1m+60+24 ").unwrap();
assert_eq!(duration, Duration::new(144, 0));
let duration = parse(" 1m + 60 + 24 ").unwrap();
assert_eq!(duration, Duration::new(144, 0))
}
}
#[cfg(feature = "cn_unit")]
#[test]
fn test_parse_unit_cn() {
let duration = parse("1å¹´").unwrap();
assert_eq!(duration, Duration::new(31536000, 0));
let duration = parse("1月").unwrap();
assert_eq!(duration, Duration::new(2592000, 0));
let duration = parse("1周").unwrap();
assert_eq!(duration, Duration::new(604800, 0));
let duration = parse("1æ—¥").unwrap();
assert_eq!(duration, Duration::new(86400, 0));
let duration = parse("1天").unwrap();
assert_eq!(duration, Duration::new(86400, 0));
let duration = parse("1æ—¶").unwrap();
assert_eq!(duration, Duration::new(3600, 0));
let duration = parse("1分").unwrap();
assert_eq!(duration, Duration::new(60, 0));
let duration = parse("1ç§’").unwrap();
assert_eq!(duration, Duration::new(1, 0));
let duration = parse("1毫秒").unwrap();
assert_eq!(duration, Duration::new(0, 1 * 1000 * 1000));
let duration = parse("1微秒").unwrap();
assert_eq!(duration, Duration::new(0, 1 * 1000));
let duration = parse("1纳秒").unwrap();
assert_eq!(duration, Duration::new(0, 1));
let duration = parse("1å¹´ 2æ—¥").unwrap();
assert_eq!(duration, Duration::new(31708800, 0));
let duration = parse("1分31秒").unwrap();
assert_eq!(duration, Duration::new(91, 0));
#[cfg(not(feature = "no_calc"))]
{
let duration = parse("1分+31秒").unwrap();
assert_eq!(duration, Duration::new(91, 0));
let duration = parse("1分+31秒+2毫秒+3纳秒").unwrap();
assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
let duration = parse(" 1分+ 31秒 + 2毫秒+ 3纳秒 ").unwrap();
assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
}
}
#[test]
fn test_duration_err() {
#[cfg(not(feature = "no_calc"))]
assert_eq!(
catch_err!(parse("0m+3-5")),
r#"
0m+3-5
^
expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
.trim()
);
#[cfg(feature = "no_calc")]
assert_eq!(
catch_err!(parse("0m3-5")),
r#"
0m3-5
^
expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
.trim()
);
assert_eq!(
catch_err!(parse("0mxyz")),
r#"
0mxyz
^
expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
.trim()
);
#[cfg(not(feature = "no_calc"))]
assert_eq!(
catch_err!(parse("3ms-2ms")),
r#"
3ms-2ms
^
expected ['+', '*']"#
.trim()
);
assert_eq!(catch_err!(parse("")), "Empty input");
}
#[test]
fn test_parse() {
let duration = parse("1d").unwrap();
assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
let duration = parse(" 1d").unwrap();
assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
let duration = parse("1d ").unwrap();
assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
let duration = parse(" 1d ").unwrap();
assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
#[cfg(not(feature = "no_calc"))]
{
let duration = parse("3m+31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3m + 31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3m + 13s + 29ms").unwrap();
assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
let duration = parse("3m + 1s + 29ms +17µs").unwrap();
assert_eq!(
duration,
Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
);
let duration = parse("1m*10").unwrap(); assert_eq!(duration, Duration::new(600, 0));
let duration = parse("1m*10ms").unwrap();
assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
let duration = parse("1m * 1ns").unwrap();
assert_eq!(duration, Duration::new(0, 60));
let duration = parse("1m * 1m").unwrap();
assert_eq!(duration, Duration::new(3600, 0));
let duration = parse("3m + 31").unwrap();
assert_eq!(duration, Duration::new(211, 0));
}
#[cfg(feature = "no_calc")]
{
let duration = parse("3m31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3m 31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
}
let duration = parse("3m 31s").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3m31s0ns").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse(" 3m 31s 0ns ").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse("1d2h3min4s").unwrap();
assert_eq!(duration, Duration::new(93784, 0));
}
#[cfg(not(feature = "no_calc"))]
#[test]
fn test_overflow_plus() {
assert_eq!(
catch_err!(parse("10000000000000000y+60")),
r#"
10000000000000000y+60
^
overflow error"#
.trim()
.to_string()
);
}
#[cfg(not(feature = "no_calc"))]
#[test]
fn test_max_mul() {
let duration = parse("580y*1").unwrap();
assert_eq!(
duration,
std::time::Duration::from_millis(18290880000) * 1000
);
}
#[cfg(not(feature = "no_calc"))]
#[test]
fn test_overflow_mul() {
let err = parse("580y*2").err().unwrap();
assert_eq!(err, "overflow error");
}
#[test]
fn test_parse_optional_spaces() {
let duration = parse("1 d").unwrap();
assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
#[cfg(not(feature = "no_calc"))]
{
let duration = parse("3 m+31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3 m + 31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3 m + 13 s + 29 ms").unwrap();
assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
let duration = parse("3 m + 1 s + 29 ms +17µs").unwrap();
assert_eq!(
duration,
Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
);
let duration = parse("1 m*10").unwrap(); assert_eq!(duration, Duration::new(600, 0));
let duration = parse("1 m*10 ms").unwrap();
assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
let duration = parse("1 m * 1ns").unwrap();
assert_eq!(duration, Duration::new(0, 60));
let duration = parse("1 m * 1 m").unwrap();
assert_eq!(duration, Duration::new(3600, 0));
let duration = parse("3 m + 31").unwrap();
assert_eq!(duration, Duration::new(211, 0));
}
#[cfg(feature = "no_calc")]
{
let duration = parse("3 m31").unwrap(); assert_eq!(duration, Duration::new(211, 0));
}
let duration = parse("3 m 31 s").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse("3 m31 s0 ns").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse(" 3 m 31 s 0 ns ").unwrap();
assert_eq!(duration, Duration::new(211, 0));
let duration = parse("1 d2 h3 min 4s").unwrap();
assert_eq!(duration, Duration::new(93784, 0));
}
}
#[cfg(all(test, feature = "chrono"))]
mod chrono_tests {
use crate::{
after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
parse_chrono,
};
use chrono::{Datelike, Utc};
#[test]
fn test_parse_chrono() {
use chrono::Duration;
#[cfg(not(feature = "no_calc"))]
let duration = parse_chrono("1m+60+24 ").unwrap();
#[cfg(feature = "no_calc")]
let duration = parse_chrono("1m84s ").unwrap();
assert_eq!(duration, Duration::seconds(144))
}
#[test]
fn test_after_naive_date_time() {
let date = Utc::now().naive_utc().date();
let jd = date.num_days_from_ce() + 180;
let date = after_naive_date_time("180d").unwrap();
assert_eq!(date.num_days_from_ce(), jd)
}
#[test]
fn test_after_naive_date() {
let date = Utc::now().naive_utc().date();
let jd = date.num_days_from_ce() + 180;
let date = after_naive_date("180d").unwrap();
assert_eq!(date.num_days_from_ce(), jd)
}
#[test]
fn test_before_naive_date_time() {
let date = Utc::now().naive_utc().date();
let jd = date.num_days_from_ce() - 180;
let date = before_naive_date_time("180d").unwrap();
assert_eq!(date.num_days_from_ce(), jd)
}
#[test]
fn test_before_naive_date() {
let date = Utc::now().naive_utc().date();
let jd = date.num_days_from_ce() - 180;
let date = before_naive_date("180d").unwrap();
assert_eq!(date.num_days_from_ce(), jd)
}
}
#[cfg(all(test, feature = "time"))]
mod time_tests {
use crate::parse_time;
use time::Duration;
#[test]
fn test_parse_time() {
#[cfg(not(feature = "no_calc"))]
{
let duration = parse_time("1m+60+24 ").unwrap();
assert_eq!(duration, Duration::seconds(144))
}
#[cfg(feature = "no_calc")]
{
let duration = parse_time("1m84 ").unwrap();
assert_eq!(duration, Duration::seconds(144))
}
}
}