Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  duration.rs   Sprache: unbekannt

 
//! Internal Helper types

use crate::{
    formats::{Flexible, Format, Strict, Strictness},
    prelude::*,
};

#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Debug))]
pub(crate) enum Sign {
    Positive,
    Negative,
}

impl Sign {
    #[allow(dead_code)]
    pub(crate) fn is_positive(&self) -> bool {
        *self == Sign::Positive
    }

    #[allow(dead_code)]
    pub(crate) fn is_negative(&self) -> bool {
        *self == Sign::Negative
    }

    pub(crate) fn apply<T>(&self, value: T) -> T
    where
        T: core::ops::Neg<Output = T>,
    {
        match *self {
            Sign::Positive => value,
            Sign::Negative => value.neg(),
        }
    }
}

#[derive(Copy, Clone)]
pub(crate) struct DurationSigned {
    pub(crate) sign: Sign,
    pub(crate) duration: Duration,
}

impl DurationSigned {
    pub(crate) fn new(sign: Sign, secs: u64, nanosecs: u32) -> Self {
        Self {
            sign,
            duration: Duration::new(secs, nanosecs),
        }
    }

    #[cfg(any(feature = "chrono_0_4", feature = "time_0_3"))]
    pub(crate) fn with_duration(sign: Sign, duration: Duration) -> Self {
        Self { sign, duration }
    }

    #[cfg(feature = "std")]
    pub(crate) fn to_system_time<'de, D>(self) -> Result<SystemTime, D::Error>
    where
        D: Deserializer<'de>,
    {
        match self.sign {
            Sign::Positive => SystemTime::UNIX_EPOCH.checked_add(self.duration),
            Sign::Negative => SystemTime::UNIX_EPOCH.checked_sub(self.duration),
        }
        .ok_or_else(|| DeError::custom("timestamp is outside the range for std::time::SystemTime"))
    }

    #[cfg(feature = "std")]
    pub(crate) fn to_std_duration<'de, D>(self) -> Result<Duration, D::Error>
    where
        D: Deserializer<'de>,
    {
        match self.sign {
            Sign::Positive => Ok(self.duration),
            Sign::Negative => Err(DeError::custom("std::time::Duration cannot be negative")),
        }
    }
}

impl From<&Duration> for DurationSigned {
    fn from(&duration: &Duration) -> Self {
        Self {
            sign: Sign::Positive,
            duration,
        }
    }
}

#[cfg(feature = "std")]
impl From<&SystemTime> for DurationSigned {
    fn from(time: &SystemTime) -> Self {
        match time.duration_since(SystemTime::UNIX_EPOCH) {
            Ok(dur) => DurationSigned {
                sign: Sign::Positive,
                duration: dur,
            },
            Err(err) => DurationSigned {
                sign: Sign::Negative,
                duration: err.duration(),
            },
        }
    }
}

impl core::ops::Mul<u32> for DurationSigned {
    type Output = DurationSigned;

    fn mul(mut self, rhs: u32) -> Self::Output {
        self.duration *= rhs;
        self
    }
}

impl core::ops::Div<u32> for DurationSigned {
    type Output = DurationSigned;

    fn div(mut self, rhs: u32) -> Self::Output {
        self.duration /= rhs;
        self
    }
}

impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<u64, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if source.sign.is_negative() {
            return Err(SerError::custom(
                "cannot serialize a negative Duration as u64",
            ));
        }

        let mut secs = source.duration.as_secs();

        // Properly round the value
        if source.duration.subsec_millis() >= 500 {
            if source.sign.is_positive() {
                secs += 1;
            } else {
                secs -= 1;
            }
        }
        secs.serialize(serializer)
    }
}

impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<i64, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut secs = source.sign.apply(source.duration.as_secs() as i64);

        // Properly round the value
        if source.duration.subsec_millis() >= 500 {
            if source.sign.is_positive() {
                secs += 1;
            } else {
                secs -= 1;
            }
        }
        secs.serialize(serializer)
    }
}

impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<f64, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut secs = source.sign.apply(source.duration.as_secs() as f64);

        // Properly round the value
        if source.duration.subsec_millis() >= 500 {
            if source.sign.is_positive() {
                secs += 1.;
            } else {
                secs -= 1.;
            }
        }
        secs.serialize(serializer)
    }
}

#[cfg(feature = "alloc")]
impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<String, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut secs = source.sign.apply(source.duration.as_secs() as i64);

        // Properly round the value
        if source.duration.subsec_millis() >= 500 {
            if source.sign.is_positive() {
                secs += 1;
            } else {
                secs -= 1;
            }
        }
        secs.to_string().serialize(serializer)
    }
}

impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<f64, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        source
            .sign
            .apply(utils::duration_as_secs_f64(&source.duration))
            .serialize(serializer)
    }
}

#[cfg(feature = "alloc")]
impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<String, STRICTNESS>
where
    STRICTNESS: Strictness,
{
    fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        source
            .sign
            .apply(utils::duration_as_secs_f64(&source.duration))
            .to_string()
            .serialize(serializer)
    }
}

macro_rules! duration_impls {
    ($($inner:ident { $($factor:literal => $outer:ident,)+ })+) => {
        $($(

        impl<FORMAT, STRICTNESS> SerializeAs<DurationSigned> for $outer<FORMAT, STRICTNESS>
        where
            FORMAT: Format,
            STRICTNESS: Strictness,
            $inner<FORMAT, STRICTNESS>: SerializeAs<DurationSigned>
        {
            fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                $inner::<FORMAT, STRICTNESS>::serialize_as(&(*source * $factor), serializer)
            }
        }

        impl<'de, FORMAT, STRICTNESS> DeserializeAs<'de, DurationSigned> for $outer<FORMAT, STRICTNESS>
        where
            FORMAT: Format,
            STRICTNESS: Strictness,
            $inner<FORMAT, STRICTNESS>: DeserializeAs<'de, DurationSigned>,
        {
            fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
            where
                D: Deserializer<'de>,
            {
                let dur = $inner::<FORMAT, STRICTNESS>::deserialize_as(deserializer)?;
                Ok(dur / $factor)
            }
        }

        )+)+    };
}
duration_impls!(
    DurationSeconds {
        1000u32 => DurationMilliSeconds,
        1_000_000u32 => DurationMicroSeconds,
        1_000_000_000u32 => DurationNanoSeconds,
    }
    DurationSecondsWithFrac {
        1000u32 => DurationMilliSecondsWithFrac,
        1_000_000u32 => DurationMicroSecondsWithFrac,
        1_000_000_000u32 => DurationNanoSecondsWithFrac,
    }
);

struct DurationVisitorFlexible;
impl<'de> Visitor<'de> for DurationVisitorFlexible {
    type Value = DurationSigned;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("an integer, a float, or a string containing a number")
    }

    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        if value >= 0 {
            Ok(DurationSigned::new(Sign::Positive, value as u64, 0))
        } else {
            Ok(DurationSigned::new(Sign::Negative, (-value) as u64, 0))
        }
    }

    fn visit_u64<E>(self, secs: u64) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        Ok(DurationSigned::new(Sign::Positive, secs, 0))
    }

    fn visit_f64<E>(self, secs: f64) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        utils::duration_signed_from_secs_f64(secs).map_err(DeError::custom)
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        match parse_float_into_time_parts(value) {
            Ok((sign, seconds, subseconds)) => Ok(DurationSigned::new(sign, seconds, subseconds)),
            Err(ParseFloatError::InvalidValue) => {
                Err(DeError::invalid_value(Unexpected::Str(value), &self))
            }
            Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
        }
    }
}

impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<u64, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        u64::deserialize(deserializer).map(|secs: u64| DurationSigned::new(Sign::Positive, secs, 0))
    }
}

impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<i64, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        i64::deserialize(deserializer).map(|mut secs: i64| {
            let mut sign = Sign::Positive;
            if secs.is_negative() {
                secs = -secs;
                sign = Sign::Negative;
            }
            DurationSigned::new(sign, secs as u64, 0)
        })
    }
}

// round() only works on std
#[cfg(feature = "std")]
impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<f64, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = f64::deserialize(deserializer)?.round();
        utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
    }
}

#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<String, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct DurationDeserializationVisitor;

        impl<'de> Visitor<'de> for DurationDeserializationVisitor {
            type Value = DurationSigned;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(formatter, "a string containing a number")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: DeError,
            {
                let mut secs: i64 = value.parse().map_err(DeError::custom)?;
                let mut sign = Sign::Positive;
                if secs.is_negative() {
                    secs = -secs;
                    sign = Sign::Negative;
                }
                Ok(DurationSigned::new(sign, secs as u64, 0))
            }
        }

        deserializer.deserialize_str(DurationDeserializationVisitor)
    }
}

impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSeconds<FORMAT, Flexible>
where
    FORMAT: Format,
{
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(DurationVisitorFlexible)
    }
}

impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<f64, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = f64::deserialize(deserializer)?;
        utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
    }
}

#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<String, Strict> {
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = String::deserialize(deserializer)?;
        match parse_float_into_time_parts(&value) {
            Ok((sign, seconds, subseconds)) => Ok(DurationSigned {
                sign,
                duration: Duration::new(seconds, subseconds),
            }),
            Err(ParseFloatError::InvalidValue) => Err(DeError::invalid_value(
                Unexpected::Str(&value),
                &"a string containing an integer or float",
            )),
            Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
        }
    }
}

impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<FORMAT, Flexible>
where
    FORMAT: Format,
{
    fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(DurationVisitorFlexible)
    }
}

#[cfg_attr(test, derive(Debug, PartialEq))]
pub(crate) enum ParseFloatError {
    InvalidValue,
    #[cfg(not(feature = "alloc"))]
    Custom(&'static str),
    #[cfg(feature = "alloc")]
    Custom(String),
}

fn parse_float_into_time_parts(mut value: &str) -> Result<(Sign, u64, u32), ParseFloatError> {
    let sign = match value.chars().next() {
        // Advance by the size of the parsed char
        Some('+') => {
            value = &value[1..];
            Sign::Positive
        }
        Some('-') => {
            value = &value[1..];
            Sign::Negative
        }
        _ => Sign::Positive,
    };

    let partslen = value.split('.').count();
    let mut parts = value.split('.');
    match partslen {
        1 => {
            let seconds = parts.next().expect("Float contains exactly one part");
            if let Ok(seconds) = seconds.parse() {
                Ok((sign, seconds, 0))
            } else {
                Err(ParseFloatError::InvalidValue)
            }
        }
        2 => {
            let seconds = parts.next().expect("Float contains exactly one part");
            if let Ok(seconds) = seconds.parse() {
                let subseconds = parts.next().expect("Float contains exactly one part");
                let subseclen = subseconds.chars().count() as u32;
                if subseclen > 9 {
                    #[cfg(feature = "alloc")]
                    return Err(ParseFloatError::Custom(alloc::format!(
                        "Duration and Timestamps with no more than 9 digits precision, but '{value}' has more"
                    )));
                    #[cfg(not(feature = "alloc"))]
                    return Err(ParseFloatError::Custom(
                        "Duration and Timestamps with no more than 9 digits precision",
                    ));
                }

                if let Ok(mut subseconds) = subseconds.parse() {
                    // convert subseconds to nanoseconds (10^-9), require 9 places for nanoseconds
                    subseconds *= 10u32.pow(9 - subseclen);
                    Ok((sign, seconds, subseconds))
                } else {
                    Err(ParseFloatError::InvalidValue)
                }
            } else {
                Err(ParseFloatError::InvalidValue)
            }
        }

        _ => Err(ParseFloatError::InvalidValue),
    }
}

#[test]
fn test_parse_float_into_time_parts() {
    // Test normal behavior
    assert_eq!(
        Ok((Sign::Positive, 123, 456_000_000)),
        parse_float_into_time_parts("+123.456")
    );
    assert_eq!(
        Ok((Sign::Negative, 123, 987_000)),
        parse_float_into_time_parts("-123.000987")
    );
    assert_eq!(
        Ok((Sign::Positive, 18446744073709551615, 123_456_789)),
        parse_float_into_time_parts("18446744073709551615.123456789")
    );

    // Test behavior around 0
    assert_eq!(
        Ok((Sign::Positive, 0, 456_000_000)),
        parse_float_into_time_parts("+0.456")
    );
    assert_eq!(
        Ok((Sign::Negative, 0, 987_000)),
        parse_float_into_time_parts("-0.000987")
    );
    assert_eq!(
        Ok((Sign::Positive, 0, 123_456_789)),
        parse_float_into_time_parts("0.123456789")
    );
}

[ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge