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