Skip to content

Commit

Permalink
fix #7
Browse files Browse the repository at this point in the history
  • Loading branch information
baoyachi committed Jul 3, 2022
1 parent 5376fa3 commit 73106ba
Showing 1 changed file with 135 additions and 0 deletions.
135 changes: 135 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,9 +564,58 @@ macro_rules! des_duration {
};
}

#[cfg(feature = "serde")]
macro_rules! des_option_duration {
($name:ident,$duration_type:ident,$fn_name:ident,$parse:ident) => {
struct $name;
impl<'de> serde::de::Visitor<'de> for $name {
type Value = Option<$duration_type>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("expect duration string,e.g:'1min+30'")
}

fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let s: Option<String> = Option::deserialize(d)?;
if let Some(s) = s {
let duration = $parse(s).map_err(serde::de::Error::custom)?;
return Ok(Some(duration));
}
Ok(None)
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
}

pub fn $fn_name<'de, D>(deserializer: D) -> Result<Option<$duration_type>, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_option($name)
}
};
}

#[cfg(feature = "serde")]
des_duration!(DurationStd, Duration, deserialize_duration, parse_std);

#[cfg(feature = "serde")]
des_option_duration!(
OptionDurationStd,
Duration,
deserialize_option_duration,
parse_std
);

#[cfg(all(feature = "chrono", feature = "serde"))]
des_duration!(
DurationChrono,
Expand All @@ -575,6 +624,14 @@ des_duration!(
parse_chrono
);

#[cfg(all(feature = "chrono", feature = "serde"))]
des_option_duration!(
OptionDurationChrono,
CDuration,
deserialize_option_duration_chrono,
parse_chrono
);

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -743,6 +800,84 @@ mod chrono_tests {
);
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_option_duration_chrono() {
use chrono::Duration;
use serde::*;
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_option_duration_chrono")]
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))
);
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_duration() {
use serde::*;
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_duration")]
time_ticker: Duration,
}
let json = r#"{"time_ticker":"1min+30"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(config.time_ticker, Duration::from_secs(90));
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_option_duration() {
use serde::*;
#[derive(Debug, Deserialize)]
struct Config {
#[serde(deserialize_with = "deserialize_option_duration")]
time_ticker: Option<Duration>,
}
let json = r#"{"time_ticker":"1min+30"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(config.time_ticker, Some(Duration::from_secs(90)));
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_option_duration2() {
use serde::*;
#[derive(Debug, Deserialize, PartialEq)]
struct Config {
#[serde(default, deserialize_with = "deserialize_option_duration")]
time_ticker: Option<Duration>,
name: String,
}
let json = r#"{"time_ticker":null,"name":"foo"}"#;
let config: Config = serde_json::from_str(json).unwrap();

assert_eq!(
config,
Config {
time_ticker: None,
name: "foo".into()
}
);

let json = r#"{"name":"foo"}"#;
let config: Config = serde_json::from_str(json).unwrap();
assert_eq!(
config,
Config {
time_ticker: None,
name: "foo".into()
}
);
}

#[test]
fn test_after_naive_date_time() {
let date = Utc::now().naive_utc().date();
Expand Down

0 comments on commit 73106ba

Please sign in to comment.