obws/serde/
duration_timecode.rs

1use std::fmt;
2
3use serde::{
4    de::{self, Deserializer, Visitor},
5    ser::Serializer,
6};
7use time::Duration;
8
9#[derive(Debug, thiserror::Error)]
10enum Error {
11    #[error("hours missing")]
12    HoursMissing,
13    #[error("minutes missing")]
14    MinutesMissing,
15    #[error("seconds missing")]
16    SecondsMissing,
17    #[error("milliseconds missing")]
18    MillisecondsMissing,
19    #[error("invalid integer")]
20    InvalidInteger(#[from] std::num::ParseIntError),
21}
22
23#[allow(dead_code)]
24pub fn serialize<S>(value: &Duration, serializer: S) -> Result<S::Ok, S::Error>
25where
26    S: Serializer,
27{
28    let whole_secs = value.whole_seconds();
29    let hours = whole_secs / 3600;
30    let minutes = whole_secs % 3600 / 60;
31    let seconds = whole_secs % 3600 % 60;
32    let millis = value.subsec_milliseconds();
33
34    serializer.serialize_str(&format!("{hours:02}:{minutes:02}:{seconds:02}.{millis:03}"))
35}
36
37pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
38where
39    D: Deserializer<'de>,
40{
41    deserializer.deserialize_str(DurationTimecodeVisitor)
42}
43
44struct DurationTimecodeVisitor;
45
46impl Visitor<'_> for DurationTimecodeVisitor {
47    type Value = Duration;
48
49    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        formatter.write_str("a duration formatted as 'HH:MM:SS.mmm'")
51    }
52
53    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
54    where
55        E: de::Error,
56    {
57        let duration = || -> Result<Duration, Error> {
58            let mut hms = v.splitn(3, ':');
59            let hours = hms.next().ok_or(Error::HoursMissing)?.parse()?;
60            let minutes = hms.next().ok_or(Error::MinutesMissing)?.parse()?;
61            let seconds = hms.next().ok_or(Error::SecondsMissing)?;
62
63            let mut sm = seconds.splitn(2, '.');
64            let seconds = sm.next().ok_or(Error::SecondsMissing)?.parse()?;
65            let millis = sm.next().ok_or(Error::MillisecondsMissing)?.parse()?;
66
67            Ok(Duration::hours(hours)
68                + Duration::minutes(minutes)
69                + Duration::seconds(seconds)
70                + Duration::milliseconds(millis))
71        };
72
73        duration().map_err(de::Error::custom)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use serde::{Deserialize, Serialize};
80    use serde_test::{assert_tokens, Token};
81    use time::Duration;
82
83    #[derive(Debug, PartialEq, Serialize, Deserialize)]
84    struct SimpleDuration {
85        #[serde(with = "super")]
86        value: Duration,
87    }
88
89    #[test]
90    fn roundtrip() {
91        assert_tokens(
92            &SimpleDuration {
93                value: Duration::hours(2)
94                    + Duration::minutes(15)
95                    + Duration::seconds(4)
96                    + Duration::milliseconds(310),
97            },
98            &[
99                Token::Struct {
100                    name: "SimpleDuration",
101                    len: 1,
102                },
103                Token::Str("value"),
104                Token::Str("02:15:04.310"),
105                Token::StructEnd,
106            ],
107        );
108    }
109}