From 3d44ff47b6ac00c06634c22492ab868b522668f8 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 14 Jan 2025 18:24:28 +0100 Subject: [PATCH] feat: optional spaces between digits and unit This updates the parser to allow optional spaces between digits and unit, so that "1 month", "1 month 2 days", and similar constructs will parse successfully. Signed-off-by: Gergely Nagy --- src/parser.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++---- src/serde.rs | 16 +++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index b558f7d..1339a7a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -28,7 +28,7 @@ fn opt_cond_unit<'a>(input: &mut &'a str) -> PResult> return Ok(CondUnit::Plus); } - if peek((multispace, digit1, opt_unit_abbr, multispace)) + if peek((multispace, digit1, multispace0, opt_unit_abbr, multispace)) .parse_next(input) .is_ok() { @@ -41,8 +41,8 @@ fn opt_cond_unit<'a>(input: &mut &'a str) -> PResult> } pub(crate) fn parse_expr_time<'a>(input: &mut &'a str) -> PResult> { - (multispace0, digit1, opt_unit_abbr, multispace0) - .map(|x| (x.1, x.2)) + (multispace0, digit1, multispace0, opt_unit_abbr, multispace0) + .map(|x| (x.1, x.3)) .try_map(|(v, unit)| unit.duration(v)) .parse_next(input) } @@ -57,12 +57,13 @@ pub(crate) fn cond_time<'a>( opt_cond_unit, multispace0, digit1, + multispace0, // Add by default. // Parse unit, default is seconds. opt_unit_abbr, multispace0, ) - .map(|x| (x.3, x.1, x.4)), + .map(|x| (x.3, x.1, x.5)), ) .fold(Vec::new, |mut acc: Vec<_>, item| { acc.push(item); @@ -282,6 +283,54 @@ partial_input:10000000000000000y+60, overflow error"# 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)); + + let duration = parse("3 m+31").unwrap(); //the default duration unit is second. + assert_eq!(duration, Duration::new(211, 0)); + + let duration = parse("3 m + 31").unwrap(); //the default duration unit is second. + 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(); //the default duration unit is second. + 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)); + + 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"))] diff --git a/src/serde.rs b/src/serde.rs index 628bd7a..6690506 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -164,6 +164,22 @@ mod tests { ); } + #[cfg(feature = "serde")] + #[test] + fn test_deserialize_unit_with_spaces() { + #[derive(Debug, Deserialize)] + struct Config { + #[serde(deserialize_with = "deserialize_duration_time")] + time_ticker: TDuration, + } + let json = r#"{"time_ticker":"1 y + 30"}"#; + let config: Config = serde_json::from_str(json).unwrap(); + assert_eq!( + config.time_ticker, + TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30) + ); + } + #[cfg(all(feature = "serde", feature = "chrono"))] #[test] fn test_deserialize_duration_chrono() {