1#[cfg(all(feature = "chrono", feature = "serde"))]
2use crate::parse_chrono;
3use crate::parse_std;
4#[cfg(all(feature = "time", feature = "serde"))]
5use crate::parse_time;
6use std::time::Duration;
7
8#[cfg(all(feature = "chrono", feature = "serde"))]
9use chrono::Duration as CDuration;
10
11#[cfg(all(feature = "time", feature = "serde"))]
12use time::Duration as TDuration;
13
14#[cfg(feature = "serde")]
16pub trait DeserializeDuration<'de>: Sized {
17 fn deserialize_duration<D>(deserializer: D) -> Result<Self, D::Error>
19 where
20 D: serde::Deserializer<'de>;
21}
22
23#[cfg(feature = "serde")]
25macro_rules! impl_deserialize_duration {
26 ($duration_type:ty, $parse:ident) => {
27 impl<'de> DeserializeDuration<'de> for $duration_type {
28 fn deserialize_duration<D>(deserializer: D) -> Result<Self, D::Error>
29 where
30 D: serde::Deserializer<'de>,
31 {
32 struct DurationVisitor;
33 impl<'de> serde::de::Visitor<'de> for DurationVisitor {
34 type Value = $duration_type;
35
36 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
37 formatter.write_str("expect duration string, e.g: '1min+30'")
38 }
39
40 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
41 where
42 E: serde::de::Error,
43 {
44 let duration = $parse(s).map_err(serde::de::Error::custom)?;
45 Ok(duration)
46 }
47 }
48
49 deserializer.deserialize_any(DurationVisitor)
50 }
51 }
52 };
53}
54
55#[cfg(feature = "serde")]
57macro_rules! impl_deserialize_option_duration {
58 ($duration_type:ty, $parse:ident) => {
59 impl<'de> DeserializeDuration<'de> for Option<$duration_type> {
60 fn deserialize_duration<D>(deserializer: D) -> Result<Self, D::Error>
61 where
62 D: serde::Deserializer<'de>,
63 {
64 struct OptionDurationVisitor;
65 impl<'de> serde::de::Visitor<'de> for OptionDurationVisitor {
66 type Value = Option<$duration_type>;
67
68 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
69 formatter.write_str("expect duration string, null, or missing field")
70 }
71
72 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
73 where
74 E: serde::de::Error,
75 {
76 if s.is_empty() {
77 return Ok(None);
78 }
79 let duration = $parse(s).map_err(serde::de::Error::custom)?;
80 Ok(Some(duration))
81 }
82
83 fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
84 where
85 D: serde::Deserializer<'de>,
86 {
87 use serde::Deserialize;
88 let s: String = String::deserialize(d)?;
89 if s.is_empty() {
90 return Ok(None);
91 }
92 let duration = $parse(s).map_err(serde::de::Error::custom)?;
93 Ok(Some(duration))
94 }
95
96 fn visit_none<E>(self) -> Result<Self::Value, E>
97 where
98 E: serde::de::Error,
99 {
100 Ok(None)
101 }
102 }
103
104 deserializer.deserialize_option(OptionDurationVisitor)
105 }
106 }
107 };
108}
109
110#[cfg(feature = "serde")]
124pub fn deserialize_duration<'de, D, T>(deserializer: D) -> Result<T, D::Error>
125where
126 D: serde::Deserializer<'de>,
127 T: DeserializeDuration<'de>,
128{
129 T::deserialize_duration(deserializer)
130}
131
132#[cfg(feature = "serde")]
135impl_deserialize_duration!(Duration, parse_std);
136
137#[cfg(feature = "serde")]
138impl_deserialize_option_duration!(Duration, parse_std);
139
140#[cfg(all(feature = "chrono", feature = "serde"))]
143impl_deserialize_duration!(CDuration, parse_chrono);
144
145#[cfg(all(feature = "chrono", feature = "serde"))]
146impl_deserialize_option_duration!(CDuration, parse_chrono);
147
148#[cfg(all(feature = "time", feature = "serde"))]
151impl_deserialize_duration!(TDuration, parse_time);
152
153#[cfg(all(feature = "time", feature = "serde"))]
154impl_deserialize_option_duration!(TDuration, parse_time);
155
156#[cfg(all(feature = "chrono", feature = "serde"))]
162pub fn deserialize_duration_chrono<'de, D>(deserializer: D) -> Result<CDuration, D::Error>
163where
164 D: serde::Deserializer<'de>,
165{
166 DeserializeDuration::deserialize_duration(deserializer)
167}
168
169#[cfg(all(feature = "time", feature = "serde"))]
173pub fn deserialize_duration_time<'de, D>(deserializer: D) -> Result<TDuration, D::Error>
174where
175 D: serde::Deserializer<'de>,
176{
177 DeserializeDuration::deserialize_duration(deserializer)
178}
179
180#[cfg(feature = "serde")]
184#[deprecated(since = "0.19.0", note = "Use `deserialize_duration` instead")]
185pub fn deserialize_option_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
186where
187 D: serde::Deserializer<'de>,
188{
189 DeserializeDuration::deserialize_duration(deserializer)
190}
191
192#[cfg(all(feature = "chrono", feature = "serde"))]
194#[deprecated(since = "0.19.0", note = "Use `deserialize_duration_chrono` instead")]
195pub fn deserialize_option_duration_chrono<'de, D>(
196 deserializer: D,
197) -> Result<Option<CDuration>, D::Error>
198where
199 D: serde::Deserializer<'de>,
200{
201 DeserializeDuration::deserialize_duration(deserializer)
202}
203
204#[cfg(all(feature = "time", feature = "serde"))]
206#[deprecated(since = "0.19.0", note = "Use `deserialize_duration_time` instead")]
207pub fn deserialize_option_duration_time<'de, D>(
208 deserializer: D,
209) -> Result<Option<TDuration>, D::Error>
210where
211 D: serde::Deserializer<'de>,
212{
213 DeserializeDuration::deserialize_duration(deserializer)
214}
215
216#[cfg(all(test, feature = "time"))]
217mod tests {
218 use super::*;
219 use crate::ONE_YEAR_NANOSECOND;
220 use serde::*;
221
222 #[cfg(feature = "serde")]
223 #[test]
224 fn test_deserialize_duration_time() {
225 #[derive(Debug, Deserialize)]
226 struct Config {
227 #[serde(deserialize_with = "deserialize_duration")]
228 time_ticker: TDuration,
229 }
230 #[cfg(not(feature = "no_calc"))]
231 let json = r#"{"time_ticker":"1y+30"}"#;
232 #[cfg(feature = "no_calc")]
233 let json = r#"{"time_ticker":"1y30"}"#;
234 let config: Config = serde_json::from_str(json).unwrap();
235 assert_eq!(
236 config.time_ticker,
237 TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
238 );
239 }
240
241 #[cfg(feature = "serde")]
242 #[test]
243 fn test_deserialize_option_duration_time() {
244 use TDuration;
245
246 #[derive(Debug, Deserialize)]
247 struct Config {
248 #[serde(default, deserialize_with = "deserialize_duration")]
249 time_ticker: Option<TDuration>,
250 }
251 #[cfg(not(feature = "no_calc"))]
252 let json = r#"{"time_ticker":"1y+30"}"#;
253 #[cfg(feature = "no_calc")]
254 let json = r#"{"time_ticker":"1y30"}"#;
255 let config: Config = serde_json::from_str(json).unwrap();
256 assert_eq!(
257 config.time_ticker,
258 Some(TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30))
259 );
260 }
261
262 #[cfg(feature = "serde")]
263 #[test]
264 fn test_deserialize_option_duration_time_null() {
265 use TDuration;
266
267 #[derive(Debug, Deserialize, PartialEq)]
268 struct Config {
269 #[serde(default, deserialize_with = "deserialize_duration")]
270 time_ticker: Option<TDuration>,
271 name: String,
272 }
273
274 let json = r#"{"time_ticker":null,"name":"foo"}"#;
276 let config: Config = serde_json::from_str(json).unwrap();
277 assert_eq!(config.time_ticker, None);
278
279 let json = r#"{"name":"foo"}"#;
281 let config: Config = serde_json::from_str(json).unwrap();
282 assert_eq!(config.time_ticker, None);
283
284 let json = r#"{"time_ticker":"","name":"foo"}"#;
286 let config: Config = serde_json::from_str(json).unwrap();
287 assert_eq!(config.time_ticker, None);
288 }
289
290 #[cfg(feature = "serde")]
291 #[test]
292 fn test_deserialize_unit_with_spaces() {
293 #[derive(Debug, Deserialize)]
294 struct Config {
295 #[serde(deserialize_with = "deserialize_duration")]
296 time_ticker: TDuration,
297 }
298 #[cfg(not(feature = "no_calc"))]
299 let json = r#"{"time_ticker":"1 y + 30"}"#;
300 #[cfg(feature = "no_calc")]
301 let json = r#"{"time_ticker":"1 y 30"}"#;
302 let config: Config = serde_json::from_str(json).unwrap();
303 assert_eq!(
304 config.time_ticker,
305 TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
306 );
307 }
308
309 #[cfg(all(feature = "serde", feature = "chrono"))]
310 #[test]
311 fn test_deserialize_duration_chrono() {
312 use chrono::Duration;
313 #[derive(Debug, serde::Deserialize)]
314 struct Config {
315 #[serde(deserialize_with = "deserialize_duration")]
316 time_ticker: Duration,
317 }
318 #[cfg(not(feature = "no_calc"))]
319 let json = r#"{"time_ticker":"1y+30"}"#;
320 #[cfg(feature = "no_calc")]
321 let json = r#"{"time_ticker":"1y30"}"#;
322 let config: Config = serde_json::from_str(json).unwrap();
323 assert_eq!(
324 config.time_ticker,
325 Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30)
326 );
327 }
328
329 #[cfg(all(feature = "serde", feature = "chrono"))]
330 #[test]
331 fn test_deserialize_option_duration_chrono() {
332 use chrono::Duration;
333 #[derive(Debug, serde::Deserialize)]
334 struct Config {
335 #[serde(default, deserialize_with = "deserialize_duration")]
336 time_ticker: Option<Duration>,
337 }
338 #[cfg(not(feature = "no_calc"))]
339 let json = r#"{"time_ticker":"1y+30"}"#;
340 #[cfg(feature = "no_calc")]
341 let json = r#"{"time_ticker":"1y30"}"#;
342 let config: Config = serde_json::from_str(json).unwrap();
343 assert_eq!(
344 config.time_ticker,
345 Some(Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30))
346 );
347 }
348
349 #[cfg(feature = "serde")]
350 #[test]
351 fn test_deserialize_duration() {
352 #[derive(Debug, serde::Deserialize)]
353 struct Config {
354 #[serde(deserialize_with = "deserialize_duration")]
355 time_ticker: std::time::Duration,
356 }
357
358 #[cfg(not(feature = "no_calc"))]
359 let json = r#"{"time_ticker":"1min+30"}"#;
360 #[cfg(feature = "no_calc")]
361 let json = r#"{"time_ticker":"1min30"}"#;
362 let config: Config = serde_json::from_str(json).unwrap();
363 assert_eq!(config.time_ticker, std::time::Duration::from_secs(90));
364 }
365
366 #[cfg(feature = "serde")]
367 #[test]
368 fn test_deserialize_option_duration() {
369 #[derive(Debug, serde::Deserialize)]
370 struct Config {
371 #[serde(default, deserialize_with = "deserialize_duration")]
372 time_ticker: Option<std::time::Duration>,
373 }
374 #[cfg(not(feature = "no_calc"))]
375 let json = r#"{"time_ticker":"1min+30"}"#;
376 #[cfg(feature = "no_calc")]
377 let json = r#"{"time_ticker":"1min30"}"#;
378 let config: Config = serde_json::from_str(json).unwrap();
379 assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
380 }
381
382 #[cfg(feature = "serde")]
383 #[test]
384 fn test_deserialize_duration2() {
385 #[derive(Debug, serde::Deserialize)]
386 struct Config {
387 #[serde(deserialize_with = "deserialize_duration")]
388 time_ticker: std::time::Duration,
389 }
390 #[cfg(not(feature = "no_calc"))]
391 let json = r#"{"time_ticker":"1y+30"}"#;
392 #[cfg(feature = "no_calc")]
393 let json = r#"{"time_ticker":"1y30"}"#;
394 let config: Config = serde_json::from_str(json).unwrap();
395 assert_eq!(
396 config.time_ticker,
397 std::time::Duration::from_nanos(ONE_YEAR_NANOSECOND)
398 + std::time::Duration::from_secs(30)
399 );
400 }
401
402 #[cfg(feature = "serde")]
403 #[test]
404 fn test_deserialize_option_duration2() {
405 #[derive(Debug, serde::Deserialize, PartialEq)]
406 struct Config {
407 #[serde(default, deserialize_with = "deserialize_duration")]
408 time_ticker: Option<std::time::Duration>,
409 name: String,
410 }
411 let json = r#"{"time_ticker":null,"name":"foo"}"#;
412 let config: Config = serde_json::from_str(json).unwrap();
413
414 assert_eq!(
415 config,
416 Config {
417 time_ticker: None,
418 name: "foo".into(),
419 }
420 );
421
422 let json = r#"{"name":"foo"}"#;
423 let config: Config = serde_json::from_str(json).unwrap();
424 assert_eq!(
425 config,
426 Config {
427 time_ticker: None,
428 name: "foo".into(),
429 }
430 );
431 }
432
433 #[cfg(feature = "serde")]
434 #[test]
435 fn test_deserialize_option_duration_empty_string() {
436 #[derive(Debug, serde::Deserialize, PartialEq)]
437 struct Config {
438 #[serde(default, deserialize_with = "deserialize_duration")]
439 time_ticker: Option<std::time::Duration>,
440 name: String,
441 }
442 let json = r#"{"time_ticker":"","name":"foo"}"#;
444 let config: Config = serde_json::from_str(json).unwrap();
445 assert_eq!(
446 config,
447 Config {
448 time_ticker: None,
449 name: "foo".into(),
450 }
451 );
452 }
453
454 #[cfg(feature = "serde")]
456 #[test]
457 #[allow(deprecated)]
458 fn test_deprecated_deserialize_option_duration() {
459 #[derive(Debug, serde::Deserialize)]
460 struct Config {
461 #[serde(deserialize_with = "deserialize_option_duration")]
462 time_ticker: Option<std::time::Duration>,
463 }
464 #[cfg(not(feature = "no_calc"))]
465 let json = r#"{"time_ticker":"1min+30"}"#;
466 #[cfg(feature = "no_calc")]
467 let json = r#"{"time_ticker":"1min30"}"#;
468 let config: Config = serde_json::from_str(json).unwrap();
469 assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
470 }
471}