diff --git a/Cargo.toml b/Cargo.toml index 5091c6d..73ff0ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ time = { version = "0.3.17", optional = true } serde = { version = "1.0.147", features = ["derive"], optional = true } rust_decimal = { version = "1.29.1", default-features = false } +winnow = {version = "*",features = ["debug"]} [dev-dependencies] serde_json = { version = "1.0.87" } diff --git a/src/lib.rs b/src/lib.rs index 2ded47d..ce2a62e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,6 +165,8 @@ //! } //! ``` +mod parser; + #[cfg(all(feature = "chrono", feature = "serde"))] use chrono::Duration as CDuration; @@ -174,6 +176,7 @@ use nom::sequence::tuple; use rust_decimal::prelude::ToPrimitive; use rust_decimal::Decimal; use std::convert::TryFrom; +use std::str::FromStr; use std::time::Duration; use thiserror::Error; #[cfg(all(feature = "time", feature = "serde"))] @@ -212,6 +215,31 @@ enum TimeUnit { NanoSecond, } +impl FromStr for TimeUnit { + type Err = DError; + + fn from_str(s: &str) -> Result { + match &*s.to_lowercase() { + "y" | "year" => Ok(TimeUnit::Year), + "mon" | "month" => Ok(TimeUnit::Month), + "w" | "week" => Ok(TimeUnit::Week), + "d" | "day" => Ok(TimeUnit::Day), + "h" | "hour" | "hr" => Ok(TimeUnit::Hour), + "m" | "min" | "minute" => Ok(TimeUnit::Minute), + "s" | "sec" | "second" => Ok(TimeUnit::Second), + "ms" | "msec" | "millisecond" => Ok(TimeUnit::MilliSecond), + "µs" | "µsec" | "µsecond" | "us" | "usec" | "usecond" | "microsecond" => { + Ok(TimeUnit::MicroSecond) + } + "ns" | "nsec" | "nanosecond" => Ok(TimeUnit::NanoSecond), + _ => Err(DError::ParseError(format!( + "expect one of [y,mon,w,d,h,m,s,ms,µs,us,ns] or their longer forms.but find:{}", + s, + ))), + } + } +} + const ONE_MICROSECOND_NANOSECOND: u64 = 1000; const ONE_MILLISECOND_NANOSECOND: u64 = 1000 * ONE_MICROSECOND_NANOSECOND; const ONE_SECOND_NANOSECOND: u64 = 1000 * ONE_MILLISECOND_NANOSECOND; diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..8d2f050 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,61 @@ +use crate::TimeUnit; +use winnow::stream::AsChar; +use winnow::token::take_while; +use winnow::PResult; +use winnow::Parser; + +fn time_unit_abbr(input: &mut &str) -> PResult { + take_while(1.., |c: char| c.is_alpha() || c == 'µ') + .try_map(str::parse) + .parse_next(input) +} + +#[cfg(test)] +mod tests { + use super::*; + use winnow::Partial; + + #[test] + fn test_time_unit_abbr() { + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("y")), + Ok(("", TimeUnit::Year)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("mon")), + Ok(("", TimeUnit::Month)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("w")), + Ok(("", TimeUnit::Week)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("d")), + Ok(("", TimeUnit::Day)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("h")), + Ok(("", TimeUnit::Hour)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("m")), + Ok(("", TimeUnit::Minute)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("s")), + Ok(("", TimeUnit::Second)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("ms")), + Ok(("", TimeUnit::MilliSecond)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("µs")), + Ok(("", TimeUnit::MicroSecond)) + ); + assert_eq!( + time_unit_abbr.parse_peek(&Partial::new("ns")), + Ok(("", TimeUnit::NanoSecond)) + ); + } +}