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        let json = r#"{"time_ticker":"1y+30"}"#;
142        let config: Config = serde_json::from_str(json).unwrap();
143        assert_eq!(
144            config.time_ticker,
145            TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
146        );
147    }
148
149    #[cfg(feature = "serde")]
150    #[test]
151    fn test_deserialize_option_duration_time() {
152        use TDuration;
153
154        #[derive(Debug, Deserialize)]
155        struct Config {
156            #[serde(deserialize_with = "deserialize_option_duration_time")]
157            time_ticker: Option<TDuration>,
158        }
159        let json = r#"{"time_ticker":"1y+30"}"#;
160        let config: Config = serde_json::from_str(json).unwrap();
161        assert_eq!(
162            config.time_ticker,
163            Some(TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30))
164        );
165    }
166
167    #[cfg(feature = "serde")]
168    #[test]
169    fn test_deserialize_unit_with_spaces() {
170        #[derive(Debug, Deserialize)]
171        struct Config {
172            #[serde(deserialize_with = "deserialize_duration_time")]
173            time_ticker: TDuration,
174        }
175        let json = r#"{"time_ticker":"1 y + 30"}"#;
176        let config: Config = serde_json::from_str(json).unwrap();
177        assert_eq!(
178            config.time_ticker,
179            TDuration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + TDuration::seconds(30)
180        );
181    }
182
183    #[cfg(all(feature = "serde", feature = "chrono"))]
184    #[test]
185    fn test_deserialize_duration_chrono() {
186        use chrono::Duration;
187        #[derive(Debug, serde::Deserialize)]
188        struct Config {
189            #[serde(deserialize_with = "deserialize_duration_chrono")]
190            time_ticker: Duration,
191        }
192        let json = r#"{"time_ticker":"1y+30"}"#;
193        let config: Config = serde_json::from_str(json).unwrap();
194        assert_eq!(
195            config.time_ticker,
196            Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30)
197        );
198    }
199
200    #[cfg(all(feature = "serde", feature = "chrono"))]
201    #[test]
202    fn test_deserialize_option_duration_chrono() {
203        use chrono::Duration;
204        #[derive(Debug, serde::Deserialize)]
205        struct Config {
206            #[serde(deserialize_with = "deserialize_option_duration_chrono")]
207            time_ticker: Option<Duration>,
208        }
209        let json = r#"{"time_ticker":"1y+30"}"#;
210        let config: Config = serde_json::from_str(json).unwrap();
211        assert_eq!(
212            config.time_ticker,
213            Some(Duration::nanoseconds(ONE_YEAR_NANOSECOND as i64) + Duration::seconds(30))
214        );
215    }
216
217    #[cfg(feature = "serde")]
218    #[test]
219    fn test_deserialize_duration() {
220        #[derive(Debug, serde::Deserialize)]
221        struct Config {
222            #[serde(deserialize_with = "deserialize_duration")]
223            time_ticker: std::time::Duration,
224        }
225        let json = r#"{"time_ticker":"1min+30"}"#;
226        let config: Config = serde_json::from_str(json).unwrap();
227        assert_eq!(config.time_ticker, std::time::Duration::from_secs(90));
228    }
229
230    #[cfg(feature = "serde")]
231    #[test]
232    fn test_deserialize_option_duration() {
233        #[derive(Debug, serde::Deserialize)]
234        struct Config {
235            #[serde(deserialize_with = "deserialize_option_duration")]
236            time_ticker: Option<std::time::Duration>,
237        }
238        let json = r#"{"time_ticker":"1min+30"}"#;
239        let config: Config = serde_json::from_str(json).unwrap();
240        assert_eq!(config.time_ticker, Some(std::time::Duration::from_secs(90)));
241    }
242
243    #[cfg(feature = "serde")]
244    #[test]
245    fn test_deserialize_duration2() {
246        #[derive(Debug, serde::Deserialize)]
247        struct Config {
248            #[serde(deserialize_with = "deserialize_duration")]
249            time_ticker: std::time::Duration,
250        }
251        let json = r#"{"time_ticker":"1y+30"}"#;
252        let config: Config = serde_json::from_str(json).unwrap();
253        assert_eq!(
254            config.time_ticker,
255            std::time::Duration::from_nanos(ONE_YEAR_NANOSECOND)
256                + std::time::Duration::from_secs(30)
257        );
258    }
259
260    #[cfg(feature = "serde")]
261    #[test]
262    fn test_deserialize_option_duration2() {
263        #[derive(Debug, serde::Deserialize, PartialEq)]
264        struct Config {
265            #[serde(default, deserialize_with = "deserialize_option_duration")]
266            time_ticker: Option<std::time::Duration>,
267            name: String,
268        }
269        let json = r#"{"time_ticker":null,"name":"foo"}"#;
270        let config: Config = serde_json::from_str(json).unwrap();
271
272        assert_eq!(
273            config,
274            Config {
275                time_ticker: None,
276                name: "foo".into(),
277            }
278        );
279
280        let json = r#"{"name":"foo"}"#;
281        let config: Config = serde_json::from_str(json).unwrap();
282        assert_eq!(
283            config,
284            Config {
285                time_ticker: None,
286                name: "foo".into(),
287            }
288        );
289    }
290}