cyberex 0.3.48

Utilities Library
Documentation
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() {
        // 1min
        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));

        // 2h
        let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""2h""#).unwrap();
        assert_eq!(*d, Duration::from_secs(7200));

        // 1500ms
        let d: DurationAsHuman<0, UNIT_MS> = serde_json::from_str(r#""1500ms""#).unwrap();
        assert_eq!(*d, Duration::from_millis(1500));

        // 3s
        let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""3s""#).unwrap();
        assert_eq!(*d, Duration::from_secs(3));

        // 500us (microseconds)
        let d: DurationAsHuman<0, UNIT_MS> = serde_json::from_str(r#""500us""#).unwrap();
        assert_eq!(*d, Duration::from_micros(500));

        // 1d (1 day)
        let d: DurationAsHuman<0, UNIT_S> = serde_json::from_str(r#""1d""#).unwrap();
        assert_eq!(*d, Duration::from_secs(86400));
    }
}