use chrono::{NaiveDateTime, NaiveTime};
use serde::de::{Deserialize, Deserializer, Error};
pub(crate) fn deserialize_option_string<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<String>, D::Error> {
let value: Option<String> = Deserialize::deserialize(deserializer)?;
Ok(match value.as_deref() {
Some("none") | None => None,
Some(_) => value,
})
}
pub(crate) fn deserialize_option_date_time<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<NaiveDateTime>, D::Error> {
let value: Option<String> = Deserialize::deserialize(deserializer)?;
Ok(match value.as_deref() {
Some("none") | None => None,
Some(v) => {
Some(NaiveDateTime::parse_from_str(v, "%Y-%m-%dT%H:%M:%S").map_err(D::Error::custom)?)
}
})
}
pub(crate) fn deserialize_option_time<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<NaiveTime>, D::Error> {
let value: Option<String> = Deserialize::deserialize(deserializer)?;
Ok(match value.as_deref() {
Some("none") | None => None,
Some(v) => Some(NaiveTime::parse_from_str(v, "T%H:%M:%S").map_err(D::Error::custom)?),
})
}
macro_rules! custom_serialize {
($serializer:expr, $struct_name:expr; $($k:ident => ($($v:tt)*),)*) => {
let mut len = 0;
$(
let $k = custom_serialize!(@VALUE $($v)*);
if $k.is_some() {
len += 1;
}
)*
let mut state = $serializer.serialize_struct($struct_name, len)?;
$(
if let Some(v) = $k {
state.serialize_field(stringify!($k), &v)?;
}
)*
state.end()
};
(@VALUE $v:expr) => {
$v
};
(@VALUE $v:expr, to_override) => {
$v.and_then(|adjust| match adjust {
Adjust::Override(v) => Some(v),
_ => None,
})
};
(@VALUE $v:expr, to_increment, $t:ty) => {
$v.and_then(|adjust| match adjust {
Adjust::Increment(v) => Some(v as $t),
Adjust::Decrement(v) => Some(-(v as $t)),
_ => None,
})
};
(@VALUE $v:expr, to_increment_tuple, $t:ty) => {
$v.and_then(|adjust| match adjust {
Adjust::Increment(v) => Some((v.0 as $t, v.1 as $t)),
Adjust::Decrement(v) => Some((-(v.0 as $t), -(v.1 as $t))),
_ => None,
})
};
}
#[cfg(test)]
mod tests {
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use serde_json::json;
#[test]
fn deserialize_option_string() {
let json = json!("none");
let value = super::deserialize_option_string(json).unwrap();
assert_eq!(value, None);
let json = json!(null);
let value = super::deserialize_option_string(json).unwrap();
assert_eq!(value, None);
let json = json!("test");
let value = super::deserialize_option_string(json).unwrap();
assert_eq!(value, Some("test".to_owned()));
}
#[test]
fn deserialize_option_date_time() {
let json = json!("none");
let value = super::deserialize_option_date_time(json).unwrap();
assert_eq!(value, None);
let json = json!(null);
let value = super::deserialize_option_date_time(json).unwrap();
assert_eq!(value, None);
let json = json!("2020-01-01T01:30:00");
let value = super::deserialize_option_date_time(json).unwrap();
let date = NaiveDate::from_ymd(2020, 1, 1);
let time = NaiveTime::from_hms(1, 30, 0);
assert_eq!(value, Some(NaiveDateTime::new(date, time)));
}
#[test]
fn deserialize_option_time() {
let json = json!("none");
let value = super::deserialize_option_time(json).unwrap();
assert_eq!(value, None);
let json = json!(null);
let value = super::deserialize_option_time(json).unwrap();
assert_eq!(value, None);
let json = json!("T02:00:20");
let value = super::deserialize_option_time(json).unwrap();
assert_eq!(value, Some(NaiveTime::from_hms(2, 0, 20)));
}
}