use std::time::Duration;
use newtype::NewType;
use serde::Deserialize;
use serde_with::serde_as;
pub struct DurationHuman;
impl serde_with::SerializeAs<Duration> for DurationHuman {
fn serialize_as<S>(source: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = humantime::Duration::from(source.clone()).to_string();
serializer.serialize_str(&s)
}
}
impl<'de> serde_with::DeserializeAs<'de, Duration> for DurationHuman {
fn deserialize_as<D>(deserializer: D) -> Result<Duration, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
let human_duration = s.parse::<humantime::Duration>().map_err(serde::de::Error::custom)?;
Ok(human_duration.into())
}
}
#[serde_as]
#[derive(NewType, Debug, Clone, PartialEq, Eq, Deserialize, Copy)]
pub struct DurationAsMillis<const N: u64>(#[serde_as(as = "serde_with::DurationMilliSeconds<u64>")] Duration);
impl<const N: u64> Default for DurationAsMillis<N> {
fn default() -> Self {
Self(Duration::from_millis(N))
}
}
impl<const N: u64> From<u64> for DurationAsMillis<N> {
fn from(millis: u64) -> Self {
Self(Duration::from_millis(millis))
}
}
#[serde_as]
#[derive(NewType, Debug, Clone, PartialEq, Eq, Deserialize, Copy)]
pub struct DurationAsSecs<const N: u64>(#[serde_as(as = "serde_with::DurationSeconds<u64>")] Duration);
impl<const N: u64> Default for DurationAsSecs<N> {
fn default() -> Self {
Self(Duration::from_secs(N))
}
}
impl<const N: u64> From<u64> for DurationAsSecs<N> {
fn from(secs: u64) -> Self {
Self(Duration::from_secs(secs))
}
}
pub const UNIT_S: u32 = 0;
pub const UNIT_MS: u32 = 1;
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Copy)]
pub struct DurationAsHuman<const N: u64, const U: u32>(#[serde_as(as = "DurationHuman")] Duration);
impl<const N: u64, const U: u32> Default for DurationAsHuman<N, U> {
fn default() -> Self {
match U {
UNIT_S => Self(Duration::from_secs(N)),
UNIT_MS => Self(Duration::from_millis(N)),
_ => panic!("Invalid unit"),
}
}
}
impl<const N: u64, const U: u32> From<Duration> for DurationAsHuman<N, U> {
fn from(d: Duration) -> Self {
Self(d)
}
}
impl<const N: u64, const U: u32> std::ops::Deref for DurationAsHuman<N, U> {
type Target = Duration;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: u64, const U: u32> DurationAsHuman<N, U> {
pub fn into_inner(self) -> Duration {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_case_duration_as_mills_deserilize() {
let d: DurationAsMillis<1500> = serde_json::from_str("123").unwrap();
assert_eq!(*d, Duration::from_millis(123));
assert_eq!(d.into_inner(), Duration::from_millis(123));
assert_eq!(
*serde_json::from_str::<DurationAsMillis<1500>>("xx").unwrap_or_default(),
Duration::from_millis(1500)
);
assert_eq!(DurationAsMillis::<1500>::from(500u64), 500.into());
}
#[test]
fn test_case_duration_as_secs_deserilize() {
let d: DurationAsSecs<30> = serde_json::from_str("60").unwrap();
assert_eq!(*d, Duration::from_secs(60));
assert_eq!(d.into_inner(), Duration::from_secs(60));
assert_eq!(
*serde_json::from_str::<DurationAsSecs<30>>("xx").unwrap_or_default(),
Duration::from_secs(30)
);
assert_eq!(DurationAsSecs::<30>::from(120u64), 120.into());
}
#[test]
fn test_case_duration_as_human_default_sec_deserilize() {
let d: DurationAsHuman<30, UNIT_S> = serde_json::from_str(r#""500s""#).unwrap();
assert_eq!(*d, Duration::from_secs(500));
assert_eq!(d.into_inner(), Duration::from_secs(500));
assert_eq!(
*serde_json::from_str::<DurationAsHuman<30, UNIT_S>>("xx").unwrap_or_default(),
Duration::from_secs(30)
);
assert_eq!(
DurationAsHuman::<30, UNIT_S>::from(Duration::from_secs(120)),
Duration::from_secs(120).into()
);
}
#[test]
fn test_case_duration_as_human_default_mill_deserilize() {
let d: DurationAsHuman<1500, UNIT_MS> = serde_json::from_str(r#""2000ms""#).unwrap();
assert_eq!(*d, Duration::from_millis(2000));
assert_eq!(d.into_inner(), Duration::from_millis(2000));
assert_eq!(
*serde_json::from_str::<DurationAsHuman<1500, UNIT_MS>>("xx").unwrap_or_default(),
Duration::from_millis(1500)
);
assert_eq!(
DurationAsHuman::<1500, UNIT_MS>::from(Duration::from_millis(3000)),
Duration::from_millis(3000).into()
);
}
#[test]
fn test_case_duration_as_human_deserialize_humantime_str() {
let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""1min""#).unwrap();
assert_eq!(*d, Duration::from_secs(60));
assert_eq!(d.into_inner(), Duration::from_secs(60));
let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""2h""#).unwrap();
assert_eq!(*d, Duration::from_secs(7200));
let d: DurationAsHuman<0, UNIT_MS> = serde_json::from_str(r#""1500ms""#).unwrap();
assert_eq!(*d, Duration::from_millis(1500));
let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""3s""#).unwrap();
assert_eq!(*d, Duration::from_secs(3));
let d: DurationAsHuman<0, UNIT_MS> = serde_json::from_str(r#""500us""#).unwrap();
assert_eq!(*d, Duration::from_micros(500));
let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""1d""#).unwrap();
assert_eq!(*d, Duration::from_secs(86400));
}
}