duration_str/
serde.rs

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")]
15macro_rules! des_duration {
16    ($name:ident,$duration_type:ident,$fn_name:ident,$parse:ident) => {
17        struct $name;
18        impl<'de> serde::de::Visitor<'de> for $name {
19            type Value = $duration_type;
20
21            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
22                formatter.write_str("expect duration string,e.g:'1min+30'")
23            }
24
25            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
26            where
27                E: serde::de::Error,
28            {
29                let duration = $parse(s).map_err(serde::de::Error::custom)?;
30                Ok(duration)
31            }
32        }
33
34        pub fn $fn_name<'de, D>(deserializer: D) -> Result<$duration_type, D::Error>
35        where
36            D: serde::Deserializer<'de>,
37        {
38            deserializer.deserialize_any($name)
39        }
40    };
41}
42
43#[cfg(feature = "serde")]
44macro_rules! des_option_duration {
45    ($name:ident,$duration_type:ident,$fn_name:ident,$parse:ident) => {
46        struct $name;
47        impl<'de> serde::de::Visitor<'de> for $name {
48            type Value = Option<$duration_type>;
49
50            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
51                formatter.write_str("expect duration string,e.g:'1min+30'")
52            }
53
54            fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
55            where
56                D: serde::Deserializer<'de>,
57            {
58                use serde::Deserialize;
59                let s: Option<String> = Option::deserialize(d)?;
60                if let Some(s) = s {
61                    let duration = $parse(s).map_err(serde::de::Error::custom)?;
62                    return Ok(Some(duration));
63                }
64                Ok(None)
65            }
66
67            fn visit_none<E>(self) -> Result<Self::Value, E>
68            where
69                E: serde::de::Error,
70            {
71                Ok(None)
72            }
73        }
74
75        pub fn $fn_name<'de, D>(deserializer: D) -> Result<Option<$duration_type>, D::Error>
76        where
77            D: serde::Deserializer<'de>,
78        {
79            deserializer.deserialize_option($name)
80        }
81    };
82}
83
84#[cfg(feature = "serde")]
85des_duration!(DurationStd, Duration, deserialize_duration, parse_std);
86
87#[cfg(feature = "serde")]
88des_option_duration!(
89    OptionDurationStd,
90    Duration,
91    deserialize_option_duration,
92    parse_std
93);
94
95#[cfg(all(feature = "chrono", feature = "serde"))]
96des_duration!(
97    DurationChrono,
98    CDuration,
99    deserialize_duration_chrono,
100    parse_chrono
101);
102
103#[cfg(all(feature = "chrono", feature = "serde"))]
104des_option_duration!(
105    OptionDurationChrono,
106    CDuration,
107    deserialize_option_duration_chrono,
108    parse_chrono
109);
110
111#[cfg(all(feature = "time", feature = "serde"))]
112des_duration!(
113    DurationTime,
114    TDuration,
115    deserialize_duration_time,
116    parse_time
117);
118
119#[cfg(all(feature = "time", feature = "serde"))]
120des_option_duration!(
121    OptionDurationTime,
122    TDuration,
123    deserialize_option_duration_time,
124    parse_time
125);
126
127#[cfg(all(test, feature = "time"))]
128mod tests {
129    use super::*;
130    use crate::ONE_YEAR_NANOSECOND;
131    use serde::*;
132
133    #[cfg(feature = "serde")]
134    #[test]
135    fn test_deserialize_duration_time() {
136        #[derive(Debug, Deserialize)]
137        struct Config {
138            #[serde(deserialize_with = "deserialize_duration_time")]
139            time_ticker: TDuration,
140        }
141        #[cfg(not(feature = "no_calc"))]
142        let json = r#"{"time_ticker":"1y+30"}"#;
143        #[cfg(feature = "no_calc")]
144        let json = r#"{"time_ticker":"1y30"}"#;
145        let config: Config = serde_json::from_str(json).unwrap();
146        assert_eq!(
147            config.time_ticker,
148            TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
149        );
150    }
151
152    #[cfg(feature = "serde")]
153    #[test]
154    fn test_deserialize_option_duration_time() {
155        use TDuration;
156
157        #[derive(Debug, Deserialize)]
158        struct Config {
159            #[serde(deserialize_with = "deserialize_option_duration_time")]
160            time_ticker: Option<TDuration>,
161        }
162        #[cfg(not(feature = "no_calc"))]
163        let json = r#"{"time_ticker":"1y+30"}"#;
164        #[cfg(feature = "no_calc")]
165        let json = r#"{"time_ticker":"1y30"}"#;
166        let config: Config = serde_json::from_str(json).unwrap();
167        assert_eq!(
168            config.time_ticker,
169            Some(TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30))
170        );
171    }
172
173    #[cfg(feature = "serde")]
174    #[test]
175    fn test_deserialize_unit_with_spaces() {
176        #[derive(Debug, Deserialize)]
177        struct Config {
178            #[serde(deserialize_with = "deserialize_duration_time")]
179            time_ticker: TDuration,
180        }
181        #[cfg(not(feature = "no_calc"))]
182        let json = r#"{"time_ticker":"1 y + 30"}"#;
183        #[cfg(feature = "no_calc")]
184        let json = r#"{"time_ticker":"1 y  30"}"#;
185        let config: Config = serde_json::from_str(json).unwrap();
186        assert_eq!(
187            config.time_ticker,
188            TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
189        );
190    }
191
192    #[cfg(all(feature = "serde", feature = "chrono"))]
193    #[test]
194    fn test_deserialize_duration_chrono() {
195        use chrono::Duration;
196        #[derive(Debug, serde::Deserialize)]
197        struct Config {
198            #[serde(deserialize_with = "deserialize_duration_chrono")]
199            time_ticker: Duration,
200        }
201        #[cfg(not(feature = "no_calc"))]
202        let json = r#"{"time_ticker":"1y+30"}"#;
203        #[cfg(feature = "no_calc")]
204        let json = r#"{"time_ticker":"1y30"}"#;
205        let config: Config = serde_json::from_str(json).unwrap();
206        assert_eq!(
207            config.time_ticker,
208            Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30)
209        );
210    }
211
212    #[cfg(all(feature = "serde", feature = "chrono"))]
213    #[test]
214    fn test_deserialize_option_duration_chrono() {
215        use chrono::Duration;
216        #[derive(Debug, serde::Deserialize)]
217        struct Config {
218            #[serde(deserialize_with = "deserialize_option_duration_chrono")]
219            time_ticker: Option<Duration>,
220        }
221        #[cfg(not(feature = "no_calc"))]
222        let json = r#"{"time_ticker":"1y+30"}"#;
223        #[cfg(feature = "no_calc")]
224        let json = r#"{"time_ticker":"1y30"}"#;
225        let config: Config = serde_json::from_str(json).unwrap();
226        assert_eq!(
227            config.time_ticker,
228            Some(Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30))
229        );
230    }
231
232    #[cfg(feature = "serde")]
233    #[test]
234    fn test_deserialize_duration() {
235        #[derive(Debug, serde::Deserialize)]
236        struct Config {
237            #[serde(deserialize_with = "deserialize_duration")]
238            time_ticker: std::time::Duration,
239        }
240
241        #[cfg(not(feature = "no_calc"))]
242        let json = r#"{"time_ticker":"1min+30"}"#;
243        #[cfg(feature = "no_calc")]
244        let json = r#"{"time_ticker":"1min30"}"#;
245        let config: Config = serde_json::from_str(json).unwrap();
246        assert_eq!(config.time_ticker, std::time::Duration::from_secs(90));
247    }
248
249    #[cfg(feature = "serde")]
250    #[test]
251    fn test_deserialize_option_duration() {
252        #[derive(Debug, serde::Deserialize)]
253        struct Config {
254            #[serde(deserialize_with = "deserialize_option_duration")]
255            time_ticker: Option<std::time::Duration>,
256        }
257        #[cfg(not(feature = "no_calc"))]
258        let json = r#"{"time_ticker":"1min+30"}"#;
259        #[cfg(feature = "no_calc")]
260        let json = r#"{"time_ticker":"1min30"}"#;
261        let config: Config = serde_json::from_str(json).unwrap();
262        assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
263    }
264
265    #[cfg(feature = "serde")]
266    #[test]
267    fn test_deserialize_duration2() {
268        #[derive(Debug, serde::Deserialize)]
269        struct Config {
270            #[serde(deserialize_with = "deserialize_duration")]
271            time_ticker: std::time::Duration,
272        }
273        #[cfg(not(feature = "no_calc"))]
274        let json = r#"{"time_ticker":"1y+30"}"#;
275        #[cfg(feature = "no_calc")]
276        let json = r#"{"time_ticker":"1y30"}"#;
277        let config: Config = serde_json::from_str(json).unwrap();
278        assert_eq!(
279            config.time_ticker,
280            std::time::Duration::from_nanos(ONE_YEAR_NANOSECOND)
281                + std::time::Duration::from_secs(30)
282        );
283    }
284
285    #[cfg(feature = "serde")]
286    #[test]
287    fn test_deserialize_option_duration2() {
288        #[derive(Debug, serde::Deserialize, PartialEq)]
289        struct Config {
290            #[serde(default, deserialize_with = "deserialize_option_duration")]
291            time_ticker: Option<std::time::Duration>,
292            name: String,
293        }
294        let json = r#"{"time_ticker":null,"name":"foo"}"#;
295        let config: Config = serde_json::from_str(json).unwrap();
296
297        assert_eq!(
298            config,
299            Config {
300                time_ticker: None,
301                name: "foo".into(),
302            }
303        );
304
305        let json = r#"{"name":"foo"}"#;
306        let config: Config = serde_json::from_str(json).unwrap();
307        assert_eq!(
308            config,
309            Config {
310                time_ticker: None,
311                name: "foo".into(),
312            }
313        );
314    }
315}