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_unit<E>(self) -> Result<Self::Value, E>
97 where
98 E: serde::de::Error,
99 {
100 Ok(None)
101 }
102
103 fn visit_none<E>(self) -> Result<Self::Value, E>
104 where
105 E: serde::de::Error,
106 {
107 Ok(None)
108 }
109 }
110
111 deserializer.deserialize_option(OptionDurationVisitor)
112 }
113 }
114 };
115}
116
117#[cfg(feature = "serde")]
131pub fn deserialize_duration<'de, D, T>(deserializer: D) -> Result<T, D::Error>
132where
133 D: serde::Deserializer<'de>,
134 T: DeserializeDuration<'de>,
135{
136 T::deserialize_duration(deserializer)
137}
138
139#[cfg(feature = "serde")]
142impl_deserialize_duration!(Duration, parse_std);
143
144#[cfg(feature = "serde")]
145impl_deserialize_option_duration!(Duration, parse_std);
146
147#[cfg(all(feature = "chrono", feature = "serde"))]
150impl_deserialize_duration!(CDuration, parse_chrono);
151
152#[cfg(all(feature = "chrono", feature = "serde"))]
153impl_deserialize_option_duration!(CDuration, parse_chrono);
154
155#[cfg(all(feature = "time", feature = "serde"))]
158impl_deserialize_duration!(TDuration, parse_time);
159
160#[cfg(all(feature = "time", feature = "serde"))]
161impl_deserialize_option_duration!(TDuration, parse_time);
162
163#[cfg(all(feature = "chrono", feature = "serde"))]
169pub fn deserialize_duration_chrono<'de, D>(deserializer: D) -> Result<CDuration, D::Error>
170where
171 D: serde::Deserializer<'de>,
172{
173 DeserializeDuration::deserialize_duration(deserializer)
174}
175
176#[cfg(all(feature = "time", feature = "serde"))]
180pub fn deserialize_duration_time<'de, D>(deserializer: D) -> Result<TDuration, D::Error>
181where
182 D: serde::Deserializer<'de>,
183{
184 DeserializeDuration::deserialize_duration(deserializer)
185}
186
187#[cfg(feature = "serde")]
191#[deprecated(since = "0.19.0", note = "Use `deserialize_duration` instead")]
192pub fn deserialize_option_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
193where
194 D: serde::Deserializer<'de>,
195{
196 DeserializeDuration::deserialize_duration(deserializer)
197}
198
199#[cfg(all(feature = "chrono", feature = "serde"))]
201#[deprecated(since = "0.19.0", note = "Use `deserialize_duration_chrono` instead")]
202pub fn deserialize_option_duration_chrono<'de, D>(
203 deserializer: D,
204) -> Result<Option<CDuration>, D::Error>
205where
206 D: serde::Deserializer<'de>,
207{
208 DeserializeDuration::deserialize_duration(deserializer)
209}
210
211#[cfg(all(feature = "time", feature = "serde"))]
213#[deprecated(since = "0.19.0", note = "Use `deserialize_duration_time` instead")]
214pub fn deserialize_option_duration_time<'de, D>(
215 deserializer: D,
216) -> Result<Option<TDuration>, D::Error>
217where
218 D: serde::Deserializer<'de>,
219{
220 DeserializeDuration::deserialize_duration(deserializer)
221}
222
223#[cfg(all(test, feature = "time"))]
224mod tests {
225 use super::*;
226 use crate::ONE_YEAR_NANOSECOND;
227 use serde::*;
228
229 #[cfg(feature = "serde")]
230 #[test]
231 fn test_deserialize_duration_time() {
232 #[derive(Debug, Deserialize)]
233 struct Config {
234 #[serde(deserialize_with = "deserialize_duration")]
235 time_ticker: TDuration,
236 }
237 #[cfg(not(feature = "no_calc"))]
238 let json = r#"{"time_ticker":"1y+30"}"#;
239 #[cfg(feature = "no_calc")]
240 let json = r#"{"time_ticker":"1y30"}"#;
241 let config: Config = serde_json::from_str(json).unwrap();
242 assert_eq!(
243 config.time_ticker,
244 TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
245 );
246 }
247
248 #[cfg(feature = "serde")]
249 #[test]
250 fn test_deserialize_option_duration_time() {
251 use TDuration;
252
253 #[derive(Debug, Deserialize)]
254 struct Config {
255 #[serde(default, deserialize_with = "deserialize_duration")]
256 time_ticker: Option<TDuration>,
257 }
258 #[cfg(not(feature = "no_calc"))]
259 let json = r#"{"time_ticker":"1y+30"}"#;
260 #[cfg(feature = "no_calc")]
261 let json = r#"{"time_ticker":"1y30"}"#;
262 let config: Config = serde_json::from_str(json).unwrap();
263 assert_eq!(
264 config.time_ticker,
265 Some(TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30))
266 );
267 }
268
269 #[cfg(feature = "serde")]
270 #[test]
271 fn test_deserialize_option_duration_time_null() {
272 use TDuration;
273
274 #[derive(Debug, Deserialize, PartialEq)]
275 struct Config {
276 #[serde(default, deserialize_with = "deserialize_duration")]
277 time_ticker: Option<TDuration>,
278 name: String,
279 }
280
281 let json = r#"{"time_ticker":null,"name":"foo"}"#;
283 let config: Config = serde_json::from_str(json).unwrap();
284 assert_eq!(config.time_ticker, None);
285
286 let json = r#"{"name":"foo"}"#;
288 let config: Config = serde_json::from_str(json).unwrap();
289 assert_eq!(config.time_ticker, None);
290
291 let json = r#"{"time_ticker":"","name":"foo"}"#;
293 let config: Config = serde_json::from_str(json).unwrap();
294 assert_eq!(config.time_ticker, None);
295 }
296
297 #[cfg(feature = "serde")]
298 #[test]
299 fn test_deserialize_unit_with_spaces() {
300 #[derive(Debug, Deserialize)]
301 struct Config {
302 #[serde(deserialize_with = "deserialize_duration")]
303 time_ticker: TDuration,
304 }
305 #[cfg(not(feature = "no_calc"))]
306 let json = r#"{"time_ticker":"1 y + 30"}"#;
307 #[cfg(feature = "no_calc")]
308 let json = r#"{"time_ticker":"1 y 30"}"#;
309 let config: Config = serde_json::from_str(json).unwrap();
310 assert_eq!(
311 config.time_ticker,
312 TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
313 );
314 }
315
316 #[cfg(all(feature = "serde", feature = "chrono"))]
317 #[test]
318 fn test_deserialize_duration_chrono() {
319 use chrono::Duration;
320 #[derive(Debug, serde::Deserialize)]
321 struct Config {
322 #[serde(deserialize_with = "deserialize_duration")]
323 time_ticker: Duration,
324 }
325 #[cfg(not(feature = "no_calc"))]
326 let json = r#"{"time_ticker":"1y+30"}"#;
327 #[cfg(feature = "no_calc")]
328 let json = r#"{"time_ticker":"1y30"}"#;
329 let config: Config = serde_json::from_str(json).unwrap();
330 assert_eq!(
331 config.time_ticker,
332 Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30)
333 );
334 }
335
336 #[cfg(all(feature = "serde", feature = "chrono"))]
337 #[test]
338 fn test_deserialize_option_duration_chrono() {
339 use chrono::Duration;
340 #[derive(Debug, serde::Deserialize)]
341 struct Config {
342 #[serde(default, deserialize_with = "deserialize_duration")]
343 time_ticker: Option<Duration>,
344 }
345 #[cfg(not(feature = "no_calc"))]
346 let json = r#"{"time_ticker":"1y+30"}"#;
347 #[cfg(feature = "no_calc")]
348 let json = r#"{"time_ticker":"1y30"}"#;
349 let config: Config = serde_json::from_str(json).unwrap();
350 assert_eq!(
351 config.time_ticker,
352 Some(Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30))
353 );
354 }
355
356 #[cfg(feature = "serde")]
357 #[test]
358 fn test_deserialize_duration() {
359 #[derive(Debug, serde::Deserialize)]
360 struct Config {
361 #[serde(deserialize_with = "deserialize_duration")]
362 time_ticker: std::time::Duration,
363 }
364
365 #[cfg(not(feature = "no_calc"))]
366 let json = r#"{"time_ticker":"1min+30"}"#;
367 #[cfg(feature = "no_calc")]
368 let json = r#"{"time_ticker":"1min30"}"#;
369 let config: Config = serde_json::from_str(json).unwrap();
370 assert_eq!(config.time_ticker, std::time::Duration::from_secs(90));
371 }
372
373 #[cfg(feature = "serde")]
374 #[test]
375 fn test_deserialize_option_duration() {
376 #[derive(Debug, serde::Deserialize)]
377 struct Config {
378 #[serde(default, deserialize_with = "deserialize_duration")]
379 time_ticker: Option<std::time::Duration>,
380 }
381 #[cfg(not(feature = "no_calc"))]
382 let json = r#"{"time_ticker":"1min+30"}"#;
383 #[cfg(feature = "no_calc")]
384 let json = r#"{"time_ticker":"1min30"}"#;
385 let config: Config = serde_json::from_str(json).unwrap();
386 assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
387 }
388
389 #[cfg(feature = "serde")]
390 #[test]
391 fn test_deserialize_duration2() {
392 #[derive(Debug, serde::Deserialize)]
393 struct Config {
394 #[serde(deserialize_with = "deserialize_duration")]
395 time_ticker: std::time::Duration,
396 }
397 #[cfg(not(feature = "no_calc"))]
398 let json = r#"{"time_ticker":"1y+30"}"#;
399 #[cfg(feature = "no_calc")]
400 let json = r#"{"time_ticker":"1y30"}"#;
401 let config: Config = serde_json::from_str(json).unwrap();
402 assert_eq!(
403 config.time_ticker,
404 std::time::Duration::from_nanos(ONE_YEAR_NANOSECOND)
405 + std::time::Duration::from_secs(30)
406 );
407 }
408
409 #[cfg(feature = "serde")]
410 #[test]
411 fn test_deserialize_option_duration2() {
412 #[derive(Debug, serde::Deserialize, PartialEq)]
413 struct Config {
414 #[serde(default, deserialize_with = "deserialize_duration")]
415 time_ticker: Option<std::time::Duration>,
416 name: String,
417 }
418 let json = r#"{"time_ticker":null,"name":"foo"}"#;
419 let config: Config = serde_json::from_str(json).unwrap();
420
421 assert_eq!(
422 config,
423 Config {
424 time_ticker: None,
425 name: "foo".into(),
426 }
427 );
428
429 let json = r#"{"name":"foo"}"#;
430 let config: Config = serde_json::from_str(json).unwrap();
431 assert_eq!(
432 config,
433 Config {
434 time_ticker: None,
435 name: "foo".into(),
436 }
437 );
438 }
439
440 #[cfg(feature = "serde")]
441 #[test]
442 fn test_deserialize_option_duration_empty_string() {
443 #[derive(Debug, serde::Deserialize, PartialEq)]
444 struct Config {
445 #[serde(default, deserialize_with = "deserialize_duration")]
446 time_ticker: Option<std::time::Duration>,
447 name: String,
448 }
449 let json = r#"{"time_ticker":"","name":"foo"}"#;
451 let config: Config = serde_json::from_str(json).unwrap();
452 assert_eq!(
453 config,
454 Config {
455 time_ticker: None,
456 name: "foo".into(),
457 }
458 );
459 }
460
461 #[cfg(feature = "serde")]
462 #[test]
463 fn test_deserialize_option_duration_with_null_using_struct_flatten() {
464 #[derive(Debug, serde::Deserialize, PartialEq)]
465 struct ConfigSubStruct {
466 #[serde(default, deserialize_with = "deserialize_duration")]
467 time_ticker: Option<std::time::Duration>,
468 name: String,
469 }
470 #[derive(Debug, serde::Deserialize, PartialEq)]
473 struct Config {
474 #[serde(default, flatten)]
475 config: ConfigSubStruct,
476 }
477 let json = r#"{"time_ticker":null,"name":"foo"}"#;
478 let config: Config = serde_json::from_str(json).unwrap();
479 assert_eq!(
480 config,
481 Config {
482 config: ConfigSubStruct {
483 time_ticker: None,
484 name: "foo".into(),
485 }
486 }
487 );
488 }
489
490 #[cfg(feature = "serde")]
492 #[test]
493 #[allow(deprecated)]
494 fn test_deprecated_deserialize_option_duration() {
495 #[derive(Debug, serde::Deserialize)]
496 struct Config {
497 #[serde(deserialize_with = "deserialize_option_duration")]
498 time_ticker: Option<std::time::Duration>,
499 }
500 #[cfg(not(feature = "no_calc"))]
501 let json = r#"{"time_ticker":"1min+30"}"#;
502 #[cfg(feature = "no_calc")]
503 let json = r#"{"time_ticker":"1min30"}"#;
504 let config: Config = serde_json::from_str(json).unwrap();
505 assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
506 }
507}