1use crate::Duration;
2use serde::de::Visitor;
3use serde::Serialize;
4
5impl TryFrom<Duration> for std::time::Duration {
6 type Error = std::num::TryFromIntError;
7
8 fn try_from(value: Duration) -> Result<Self, Self::Error> {
9 Ok(Self::new(
10 value.seconds.try_into()?,
11 value.nanos.try_into()?,
12 ))
13 }
14}
15
16impl From<std::time::Duration> for Duration {
17 fn from(value: std::time::Duration) -> Self {
18 Self {
19 seconds: value.as_secs() as _,
20 nanos: value.subsec_nanos() as _,
21 }
22 }
23}
24
25impl Serialize for Duration {
26 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: serde::Serializer,
29 {
30 if self.seconds != 0 && self.nanos != 0 && (self.nanos < 0) != (self.seconds < 0) {
31 return Err(serde::ser::Error::custom("Duration has inconsistent signs"));
32 }
33
34 let mut s = if self.seconds == 0 {
35 if self.nanos < 0 {
36 "-0".to_string()
37 } else {
38 "0".to_string()
39 }
40 } else {
41 self.seconds.to_string()
42 };
43
44 if self.nanos != 0 {
45 s.push('.');
46 let f = match split_nanos(self.nanos.unsigned_abs()) {
47 (millis, 0, 0) => format!("{:03}", millis),
48 (millis, micros, 0) => format!("{:03}{:03}", millis, micros),
49 (millis, micros, nanos) => format!("{:03}{:03}{:03}", millis, micros, nanos),
50 };
51 s.push_str(&f);
52 }
53
54 s.push('s');
55 serializer.serialize_str(&s)
56 }
57}
58
59struct DurationVisitor;
60
61impl<'de> Visitor<'de> for DurationVisitor {
62 type Value = Duration;
63
64 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 formatter.write_str("a duration string")
66 }
67
68 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
69 where
70 E: serde::de::Error,
71 {
72 let s = s
73 .strip_suffix('s')
74 .ok_or_else(|| serde::de::Error::custom("missing 's' suffix"))?;
75
76 let (negative, s) = match s.strip_prefix('-') {
77 Some(s) => (true, s),
78 None => (false, s),
79 };
80
81 let duration = match s.split_once('.') {
82 Some((seconds_str, decimal_str)) => {
83 let exp = 9_u32
84 .checked_sub(decimal_str.len() as u32)
85 .ok_or_else(|| serde::de::Error::custom("too many decimal places"))?;
86
87 let pow = 10_u32.pow(exp);
88 let seconds = seconds_str.parse().map_err(serde::de::Error::custom)?;
89 let decimal: u32 = decimal_str.parse().map_err(serde::de::Error::custom)?;
90
91 Duration {
92 seconds,
93 nanos: (decimal * pow) as i32,
94 }
95 }
96 None => Duration {
97 seconds: s.parse().map_err(serde::de::Error::custom)?,
98 nanos: 0,
99 },
100 };
101
102 Ok(match negative {
103 true => Duration {
104 seconds: -duration.seconds,
105 nanos: -duration.nanos,
106 },
107 false => duration,
108 })
109 }
110}
111
112impl<'de> serde::Deserialize<'de> for Duration {
113 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
114 where
115 D: serde::Deserializer<'de>,
116 {
117 deserializer.deserialize_str(DurationVisitor)
118 }
119}
120
121fn split_nanos(mut nanos: u32) -> (u32, u32, u32) {
123 let millis = nanos / 1_000_000;
124 nanos -= millis * 1_000_000;
125 let micros = nanos / 1_000;
126 nanos -= micros * 1_000;
127 (millis, micros, nanos)
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_duration() {
136 let verify = |duration: &Duration, expected: &str| {
137 assert_eq!(serde_json::to_string(duration).unwrap().as_str(), expected);
138 assert_eq!(
139 &serde_json::from_str::<Duration>(expected).unwrap(),
140 duration
141 )
142 };
143
144 let duration = Duration {
145 seconds: 0,
146 nanos: 0,
147 };
148 verify(&duration, "\"0s\"");
149
150 let duration = Duration {
151 seconds: 0,
152 nanos: 123,
153 };
154 verify(&duration, "\"0.000000123s\"");
155
156 let duration = Duration {
157 seconds: 0,
158 nanos: 123456,
159 };
160 verify(&duration, "\"0.000123456s\"");
161
162 let duration = Duration {
163 seconds: 0,
164 nanos: 123456789,
165 };
166 verify(&duration, "\"0.123456789s\"");
167
168 let duration = Duration {
169 seconds: 0,
170 nanos: -67088,
171 };
172 verify(&duration, "\"-0.000067088s\"");
173
174 let duration = Duration {
175 seconds: 121,
176 nanos: 3454,
177 };
178 verify(&duration, "\"121.000003454s\"");
179
180 let duration = Duration {
181 seconds: -90,
182 nanos: -2456301,
183 };
184 verify(&duration, "\"-90.002456301s\"");
185
186 let duration = Duration {
187 seconds: -90,
188 nanos: 234,
189 };
190 serde_json::to_string(&duration).unwrap_err();
191
192 let duration = Duration {
193 seconds: 90,
194 nanos: -234,
195 };
196 serde_json::to_string(&duration).unwrap_err();
197
198 serde_json::from_str::<Duration>("90.1234567891s").unwrap_err();
199 }
200}