Skip to content

Commit

Permalink
Merge pull request #10 from baoyachi/issue_8
Browse files Browse the repository at this point in the history
Support time crate Duration
  • Loading branch information
baoyachi authored Jul 3, 2022
2 parents a80d55a + 3614d55 commit b94a4ca
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 7 deletions.
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ repository = "https://github.com/baoyachi/duration-str"
license = "MIT AND Apache-2.0"

[features]
default = ["chrono", "serde"]
default = ["chrono", "serde", "time"]

[dependencies]
nom = "6.1.2"
anyhow = "1.0.38"
chrono = { version = "0.4.19", optional = true }
time = { version = "0.3.11", optional = true }

serde = { version = "1.0.124", features = ["derive"], optional = true }
rust_decimal = { version = "1.15", default-features = false }

Expand All @@ -35,3 +37,7 @@ required-features = ["serde"]
[[example]]
name = "deserialize_duration_chrono"
required-features = ["chrono", "serde"]

[[example]]
name = "deserialize_duration_time"
required-features = ["time", "serde"]
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
[![Crates.io](https://img.shields.io/crates/v/duration-str.svg)](https://crates.io/crates/duration-str)
[![Docs.rs](https://docs.rs/duration-str/badge.svg)](https://docs.rs/duration-str)

## Support
The [duration-str](https://crates.io/crates/duration-str) support multiple Duration:
* https://doc.rust-lang.org/stable/std/time/struct.Duration.html
* https://docs.rs/chrono/latest/chrono/struct.Duration.html
* https://docs.rs/time/latest/time/struct.Duration.html#


## Notice ⚠️
The default duration unit is second.Also use below **duration unit**
The default duration unit is second.Also use below **duration unit**

## Duration Unit List

Expand Down Expand Up @@ -133,7 +139,9 @@ fn main() {
);
}
```
Also you can use `deserialize_duration_chrono` function
Also you can use `deserialize_duration_chrono` or `deserialize_duration_time` function

### E.g:

```rust
use chrono::Duration;
Expand Down
15 changes: 15 additions & 0 deletions examples/deserialize_duration_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use duration_str::deserialize_duration_time;
use serde::*;
use time::Duration;

#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_duration_time")]
time_ticker: Duration,
}

fn main() {
let json = r#"{"time_ticker":"1m+30"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(config.time_ticker, Duration::seconds(60 + 30));
}
117 changes: 113 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
use anyhow::anyhow;
#[cfg(feature = "chrono")]
use chrono::Duration as CDuration;

use nom::{
character::complete::{digit1, multispace0},
combinator::opt,
Expand All @@ -178,6 +179,8 @@ use nom::{
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use std::time::Duration;
#[cfg(feature = "time")]
use time::Duration as TDuration;

#[cfg(feature = "chrono")]
pub use naive_date::{
Expand Down Expand Up @@ -359,7 +362,7 @@ fn cond_unit(input: &str) -> IResult<&str, CondUnit> {
}
}

fn parse_time(input: &str) -> IResult<&str, (&str, TimeUnit)> {
fn parse_expr_time(input: &str) -> IResult<&str, (&str, TimeUnit)> {
tuple((digit1, time_unit))(input)
}

Expand All @@ -385,7 +388,8 @@ fn cond_time(input: &str) -> IResult<&str, Vec<(&str, CondUnit, TimeUnit)>> {
/// parse string to `std::time::Duration`
pub fn parse(input: &str) -> anyhow::Result<Duration> {
let (in_input, ((time_str, time_unit), cond_opt)) =
tuple((parse_time, opt(cond_time)))(input).map_err(|e| anyhow!("parse error: {}", e))?;
tuple((parse_expr_time, opt(cond_time)))(input)
.map_err(|e| anyhow!("parse error: {}", e))?;
if !in_input.is_empty() && cond_opt.is_none() {
return Err(anyhow!(
"unsupported duration string: [{}], caused by: [{}],",
Expand Down Expand Up @@ -485,6 +489,50 @@ pub fn parse_chrono<S: Into<String>>(input: S) -> anyhow::Result<chrono::Duratio
Ok(duration)
}

/// convert Into<String> to `time::Duration`
///
/// # Example
///
/// ```rust
/// use duration_str::parse_time;
/// use time::Duration;
///
/// // supports units
/// let duration = parse_time("1d").unwrap();
/// assert_eq!(duration,Duration::seconds(24*60*60));
///
/// // supports addition
/// let duration = parse_time("3m+31").unwrap();
/// assert_eq!(duration,Duration::seconds(211));
///
/// // spaces are optional
/// let duration = parse_time("3m + 31").unwrap();
/// assert_eq!(duration,Duration::seconds(211));
///
/// // plus sign is optional
/// let duration = parse_time("3m 31").unwrap();
/// assert_eq!(duration,Duration::seconds(211));
///
/// // both plus and spaces are optional
/// let duration = parse_time("3m31").unwrap();
/// assert_eq!(duration,Duration::seconds(211));
///
/// // supports multiplication
/// let duration = parse_time("1m*10").unwrap();
/// assert_eq!(duration,Duration::seconds(600));
///
/// // spaces are optional
/// let duration = parse_time("1m * 10").unwrap();
/// assert_eq!(duration,Duration::seconds(600));
/// ```
#[cfg(feature = "time")]
pub fn parse_time<S: Into<String>>(input: S) -> anyhow::Result<time::Duration> {
let std_duration = parse_std(input)?;
use std::convert::TryFrom;
let duration = time::Duration::try_from(std_duration)?;
Ok(duration)
}

#[cfg(feature = "chrono")]
mod naive_date {
use crate::parse_chrono;
Expand Down Expand Up @@ -632,6 +680,22 @@ des_option_duration!(
parse_chrono
);

#[cfg(all(feature = "time", feature = "serde"))]
des_duration!(
DurationTime,
TDuration,
deserialize_duration_time,
parse_time
);

#[cfg(all(feature = "time", feature = "serde"))]
des_option_duration!(
OptionDurationTime,
TDuration,
deserialize_option_duration_time,
parse_time
);

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -644,8 +708,8 @@ mod tests {
}

#[test]
fn test_parse_time() {
let (input, (out, format)) = parse_time("123m").unwrap();
fn test_parse_expr_time() {
let (input, (out, format)) = parse_expr_time("123m").unwrap();
assert_eq!(input, "");
assert_eq!(out, "123");
assert_eq!(format, TimeUnit::Minute);
Expand Down Expand Up @@ -910,3 +974,48 @@ mod chrono_tests {
assert_eq!(date.num_days_from_ce(), jd)
}
}

#[cfg(all(test, feature = "time"))]
mod time_tests {
use super::*;
use serde::*;
use time::Duration;

#[test]
fn test_parse_time() {
let duration = parse_time("1m+60+24 ").unwrap();
assert_eq!(duration, Duration::seconds(144))
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_duration_time() {
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_duration_time")]
time_ticker: Duration,
}
let json = r#"{"time_ticker":"1y+30"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(
config.time_ticker,
Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30)
);
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_option_duration_time() {
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_option_duration_time")]
time_ticker: Option<Duration>,
}
let json = r#"{"time_ticker":"1y+30"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(
config.time_ticker,
Some(Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30))
);
}
}

0 comments on commit b94a4ca

Please sign in to comment.