obws/serde/
duration_timecode.rs1use 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}