duration_str/
parser.rs

1use crate::unit::{opt_unit_abbr, TimeUnit};
2use crate::{Calc, CondUnit, ExpectErr};
3use std::time::Duration;
4use winnow::ascii::{digit1, multispace0};
5use winnow::combinator::trace;
6use winnow::combinator::{alt, cut_err};
7use winnow::combinator::{eof, peek, repeat};
8use winnow::error::{StrContext, StrContextValue};
9use winnow::ModalResult as WResult;
10use winnow::Parser;
11
12pub(crate) fn cond_unit1(input: &mut &str) -> WResult<CondUnit> {
13    alt(('+'.value(CondUnit::Plus), '*'.value(CondUnit::Star)))
14        .context(StrContext::Expected(StrContextValue::Description(
15            CondUnit::get_expect_val(),
16        )))
17        .parse_next(input)
18}
19
20fn opt_cond_unit(input: &mut &str) -> WResult<CondUnit> {
21    let result = cond_unit1.parse_next(input);
22    if result.is_err() {
23        let multispace = multispace0::<_, _>;
24        if (multispace, eof).parse_next(input).is_ok() {
25            // The input result is empty except for spaces. Give `TimeUnit` default value
26            return Ok(CondUnit::Plus);
27        }
28
29        return cut_err(peek((
30            multispace,
31            digit1,
32            multispace0,
33            opt_unit_abbr,
34            multispace,
35        )))
36        .context(StrContext::Expected(StrContextValue::Description(
37            CondUnit::get_expect_val(),
38        )))
39        .value(CondUnit::Plus)
40        .parse_next(input);
41    }
42    result
43}
44
45pub(crate) fn parse_expr_time(input: &mut &str) -> WResult<u64> {
46    (multispace0, digit1, multispace0, opt_unit_abbr, multispace0)
47        .map(|x| (x.1, x.3))
48        .try_map(|(v, unit)| unit.duration(v))
49        .parse_next(input)
50}
51
52pub(crate) fn cond_time<'a>(input: &mut &'a str) -> WResult<Vec<(&'a str, CondUnit, TimeUnit)>> {
53    repeat(
54        0..,
55        (
56            multispace0,
57            opt_cond_unit,
58            multispace0,
59            digit1,
60            multispace0,
61            // Add by default.
62            // Parse unit, default is seconds.
63            opt_unit_abbr,
64            multispace0,
65        )
66            .map(|x| (x.3, x.1, x.5)),
67    )
68    .fold(Vec::new, |mut acc: Vec<_>, item| {
69        acc.push(item);
70        acc
71    })
72    .parse_next(input)
73}
74
75pub fn parse(input: impl AsRef<str>) -> Result<Duration, String> {
76    let input = input.as_ref();
77    let (unit_time, cond_val) = (parse_expr_time, trace("cond_time", cond_time))
78        .parse(input)
79        .map_err(|e| format!("{}", e))?;
80
81    let (init_cond, init_duration) = if cond_val.is_empty() {
82        CondUnit::init()
83    } else {
84        cond_val.calc().map_err(|err| err.to_string())?
85    };
86
87    let duration = init_cond
88        .calc(unit_time, init_duration)
89        .map_err(|err| err.to_string())?;
90    Ok(duration)
91}
92
93#[cfg(test)]
94#[allow(clippy::identity_op)]
95mod tests {
96    use super::*;
97    use crate::{catch_err, unit::TimeUnit, CondUnit};
98
99    #[test]
100    fn test_parse_expr_time() {
101        let (input, val) = parse_expr_time.parse_peek("123m").unwrap();
102        assert_eq!(input, "");
103        assert_eq!(val, 7380000000000);
104    }
105
106    #[test]
107    fn test_cond_unit() {
108        let (input, format) = cond_unit1.parse_peek("*123").unwrap();
109        assert_eq!(input, "123");
110        assert_eq!(format, CondUnit::Star);
111    }
112
113    #[test]
114    fn test_cond_time() {
115        let (input, out) = cond_time.parse_peek(" * 60").unwrap();
116        assert_eq!(input, "");
117        assert_eq!(out, vec![("60", CondUnit::Star, TimeUnit::Second)]);
118    }
119
120    #[test]
121    fn test_cond_time2() {
122        let (input, out) = cond_time.parse_peek(" * 60*30").unwrap();
123        assert_eq!(input, "");
124        assert_eq!(
125            out,
126            vec![
127                ("60", CondUnit::Star, TimeUnit::Second),
128                ("30", CondUnit::Star, TimeUnit::Second),
129            ]
130        );
131    }
132
133    #[test]
134    fn test_duration_parse0() {
135        let duration = parse("0").unwrap();
136        assert_eq!(duration, Duration::new(0, 0));
137
138        let duration = parse("0    ").unwrap();
139        assert_eq!(duration, Duration::new(0, 0));
140
141        let duration = parse("     0    ").unwrap();
142        assert_eq!(duration, Duration::new(0, 0));
143
144        let duration = parse("1").unwrap();
145        assert_eq!(duration, Duration::new(1, 0));
146
147        let duration = parse("0m").unwrap();
148        assert_eq!(duration, Duration::new(0, 0));
149
150        let duration = parse("1hr").unwrap();
151        assert_eq!(duration, Duration::new(3600, 0));
152
153        let duration = parse("1m+31").unwrap();
154        assert_eq!(duration, Duration::new(91, 0));
155
156        let duration = parse("1m31").unwrap();
157        assert_eq!(duration, Duration::new(91, 0));
158
159        let duration = parse("1m31s").unwrap();
160        assert_eq!(duration, Duration::new(91, 0));
161
162        let duration = parse("1m*60").unwrap();
163        assert_eq!(duration, Duration::new(3600, 0));
164
165        let duration = parse("1m*60*20").unwrap();
166        assert_eq!(duration, Duration::new(72000, 0));
167
168        let duration = parse("1m+60+24").unwrap();
169        assert_eq!(duration, Duration::new(144, 0));
170
171        let duration = parse("1m+60+24 ").unwrap();
172        assert_eq!(duration, Duration::new(144, 0));
173
174        let duration = parse("      1m      +  60 +             24 ").unwrap();
175        assert_eq!(duration, Duration::new(144, 0))
176    }
177
178    #[test]
179    fn test_duration_err() {
180        assert_eq!(
181            catch_err!(parse("0m+3-5")),
182            r#"
1830m+3-5
184    ^
185expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
186                .trim()
187        );
188
189        assert_eq!(
190            catch_err!(parse("0mxyz")),
191            r#"
1920mxyz
193 ^
194expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
195                .trim()
196        );
197
198        assert_eq!(
199            catch_err!(parse("3ms-2ms")),
200            r#"
2013ms-2ms
202   ^
203expected ['+', '*']"#
204                .trim()
205        );
206    }
207
208    #[test]
209    fn test_parse() {
210        let duration = parse("1d").unwrap();
211        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
212
213        let duration = parse("   1d").unwrap();
214        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
215
216        let duration = parse("1d   ").unwrap();
217        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
218
219        let duration = parse("   1d   ").unwrap();
220        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
221
222        let duration = parse("3m+31").unwrap(); //the default duration unit is second.
223        assert_eq!(duration, Duration::new(211, 0));
224
225        let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
226        assert_eq!(duration, Duration::new(211, 0));
227
228        let duration = parse("3m + 13s + 29ms").unwrap();
229        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
230
231        let duration = parse("3m + 1s + 29ms +17µs").unwrap();
232        assert_eq!(
233            duration,
234            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
235        );
236
237        let duration = parse("1m*10").unwrap(); //the default duration unit is second.
238        assert_eq!(duration, Duration::new(600, 0));
239
240        let duration = parse("1m*10ms").unwrap();
241        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
242
243        let duration = parse("1m * 1ns").unwrap();
244        assert_eq!(duration, Duration::new(0, 60));
245
246        let duration = parse("1m * 1m").unwrap();
247        assert_eq!(duration, Duration::new(3600, 0));
248
249        let duration = parse("3m + 31").unwrap();
250        assert_eq!(duration, Duration::new(211, 0));
251
252        let duration = parse("3m  31s").unwrap();
253        assert_eq!(duration, Duration::new(211, 0));
254
255        let duration = parse("3m31s0ns").unwrap();
256        assert_eq!(duration, Duration::new(211, 0));
257
258        let duration = parse("  3m 31s 0ns ").unwrap();
259        assert_eq!(duration, Duration::new(211, 0));
260
261        let duration = parse("1d2h3min4s").unwrap();
262        assert_eq!(duration, Duration::new(93784, 0));
263    }
264
265    #[test]
266    fn test_overflow_plus() {
267        assert_eq!(
268            catch_err!(parse("10000000000000000y+60")),
269            r#"
27010000000000000000y+60
271^
272overflow error"#
273                .trim()
274                .to_string()
275        );
276    }
277
278    #[test]
279    fn test_max_mul() {
280        let duration = parse("580y*1").unwrap();
281        assert_eq!(
282            duration,
283            std::time::Duration::from_millis(18290880000) * 1000
284        );
285    }
286
287    #[test]
288    fn test_overflow_mul() {
289        let err = parse("580y*2").err().unwrap();
290        assert_eq!(err, "overflow error");
291    }
292
293    #[test]
294    fn test_parse_optional_spaces() {
295        let duration = parse("1 d").unwrap();
296        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
297
298        let duration = parse("3 m+31").unwrap(); //the default duration unit is second.
299        assert_eq!(duration, Duration::new(211, 0));
300
301        let duration = parse("3 m + 31").unwrap(); //the default duration unit is second.
302        assert_eq!(duration, Duration::new(211, 0));
303
304        let duration = parse("3 m + 13 s + 29 ms").unwrap();
305        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
306
307        let duration = parse("3 m + 1 s + 29 ms +17µs").unwrap();
308        assert_eq!(
309            duration,
310            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
311        );
312
313        let duration = parse("1 m*10").unwrap(); //the default duration unit is second.
314        assert_eq!(duration, Duration::new(600, 0));
315
316        let duration = parse("1 m*10 ms").unwrap();
317        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
318
319        let duration = parse("1 m * 1ns").unwrap();
320        assert_eq!(duration, Duration::new(0, 60));
321
322        let duration = parse("1 m * 1 m").unwrap();
323        assert_eq!(duration, Duration::new(3600, 0));
324
325        let duration = parse("3 m + 31").unwrap();
326        assert_eq!(duration, Duration::new(211, 0));
327
328        let duration = parse("3 m  31 s").unwrap();
329        assert_eq!(duration, Duration::new(211, 0));
330
331        let duration = parse("3 m31 s0 ns").unwrap();
332        assert_eq!(duration, Duration::new(211, 0));
333
334        let duration = parse("  3 m 31 s 0 ns ").unwrap();
335        assert_eq!(duration, Duration::new(211, 0));
336
337        let duration = parse("1 d2 h3 min 4s").unwrap();
338        assert_eq!(duration, Duration::new(93784, 0));
339    }
340}
341
342#[cfg(all(test, feature = "chrono"))]
343mod chrono_tests {
344    use crate::{
345        after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
346        parse_chrono,
347    };
348    use chrono::{Datelike, Utc};
349
350    #[test]
351    fn test_parse_chrono() {
352        use chrono::Duration;
353        let duration = parse_chrono("1m+60+24 ").unwrap();
354        assert_eq!(duration, Duration::seconds(144))
355    }
356
357    #[test]
358    fn test_after_naive_date_time() {
359        let date = Utc::now().naive_utc().date();
360        let jd = date.num_days_from_ce() + 180;
361        let date = after_naive_date_time("180d").unwrap();
362        assert_eq!(date.num_days_from_ce(), jd)
363    }
364
365    #[test]
366    fn test_after_naive_date() {
367        let date = Utc::now().naive_utc().date();
368        let jd = date.num_days_from_ce() + 180;
369        let date = after_naive_date("180d").unwrap();
370        assert_eq!(date.num_days_from_ce(), jd)
371    }
372
373    #[test]
374    fn test_before_naive_date_time() {
375        let date = Utc::now().naive_utc().date();
376        let jd = date.num_days_from_ce() - 180;
377        let date = before_naive_date_time("180d").unwrap();
378        assert_eq!(date.num_days_from_ce(), jd)
379    }
380
381    #[test]
382    fn test_before_naive_date() {
383        let date = Utc::now().naive_utc().date();
384        let jd = date.num_days_from_ce() - 180;
385        let date = before_naive_date("180d").unwrap();
386        assert_eq!(date.num_days_from_ce(), jd)
387    }
388}
389
390#[cfg(all(test, feature = "time"))]
391mod time_tests {
392    use crate::parse_time;
393    use time::Duration;
394
395    #[test]
396    fn test_parse_time() {
397        let duration = parse_time("1m+60+24 ").unwrap();
398        assert_eq!(duration, Duration::seconds(144))
399    }
400}