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}