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::{alt, cut_err};
6use winnow::combinator::{eof, peek, repeat};
7use winnow::error::{ContextError, StrContext, StrContextValue};
8use winnow::ModalResult as WResult;
9use winnow::Parser;
10
11pub(crate) fn cond_unit1(input: &mut &str) -> WResult<CondUnit> {
12    alt(('+'.value(CondUnit::Plus), '*'.value(CondUnit::Star)))
13        .context(StrContext::Expected(StrContextValue::Description(
14            CondUnit::get_expect_val(),
15        )))
16        .parse_next(input)
17}
18
19fn opt_cond_unit(input: &mut &str) -> WResult<CondUnit> {
20    let result = cond_unit1.parse_next(input);
21    if result.is_err() {
22        multispace0.parse_next(input)?;
23        if eof::<_, ContextError>.parse_next(input).is_ok() {
24            // The input result is empty except for spaces. Give `TimeUnit` default value
25            return Ok(CondUnit::Plus);
26        }
27
28        return cut_err(peek((digit1, multispace0, opt_unit_abbr)))
29            .context(StrContext::Expected(StrContextValue::Description(
30                CondUnit::get_expect_val(),
31            )))
32            .value(CondUnit::Plus)
33            .parse_next(input);
34    }
35    result
36}
37
38pub(crate) fn parse_expr_time(input: &mut &str) -> WResult<u64> {
39    (multispace0, digit1, multispace0, opt_unit_abbr, multispace0)
40        .map(|x| (x.1, x.3))
41        .try_map(|(v, unit)| unit.duration(v))
42        .parse_next(input)
43}
44
45pub(crate) fn cond_time<'a>(input: &mut &'a str) -> WResult<Vec<(&'a str, CondUnit, TimeUnit)>> {
46    repeat(
47        0..,
48        (
49            multispace0,
50            opt_cond_unit,
51            multispace0,
52            digit1,
53            multispace0,
54            // Add by default.
55            // Parse unit, default is seconds.
56            opt_unit_abbr,
57            multispace0,
58        )
59            .map(|x| (x.3, x.1, x.5)),
60    )
61    .fold(Vec::new, |mut acc: Vec<_>, item| {
62        acc.push(item);
63        acc
64    })
65    .parse_next(input)
66}
67
68pub fn parse(input: impl AsRef<str>) -> Result<Duration, String> {
69    let input = input.as_ref();
70    if input.is_empty() {
71        return Err(String::from("Empty input"));
72    }
73
74    #[cfg(feature = "no_calc")]
75    {
76        use crate::DError;
77
78        let d = repeat(0.., parse_expr_time)
79            .try_fold(
80                Default::default,
81                |mut acc: u64, item| -> Result<_, DError> {
82                    acc = acc.checked_add(item).ok_or(DError::OverflowError)?;
83                    Ok(acc)
84                },
85            )
86            .parse(input)
87            .map_err(|err| err.to_string())?;
88        Ok(Duration::from_nanos(d))
89    }
90
91    #[cfg(not(feature = "no_calc"))]
92    {
93        let (unit_time, cond_val) = (parse_expr_time, cond_time)
94            .parse(input)
95            .map_err(|e| format!("{}", e))?;
96
97        let (init_cond, init_duration) = if cond_val.is_empty() {
98            CondUnit::init()
99        } else {
100            cond_val.calc().map_err(|err| err.to_string())?
101        };
102
103        let duration = init_cond
104            .calc(unit_time, init_duration)
105            .map_err(|err| err.to_string())?;
106        Ok(duration)
107    }
108}
109
110#[cfg(test)]
111#[allow(clippy::identity_op)]
112mod tests {
113    use super::*;
114    use crate::{catch_err, unit::TimeUnit, CondUnit};
115
116    #[test]
117    fn test_parse_expr_time() {
118        let (input, val) = parse_expr_time.parse_peek("123m").unwrap();
119        assert_eq!(input, "");
120        assert_eq!(val, 7380000000000);
121    }
122
123    #[test]
124    fn test_cond_unit() {
125        let (input, format) = cond_unit1.parse_peek("*123").unwrap();
126        assert_eq!(input, "123");
127        assert_eq!(format, CondUnit::Star);
128    }
129
130    #[test]
131    fn test_cond_time() {
132        let (input, out) = cond_time.parse_peek(" * 60").unwrap();
133        assert_eq!(input, "");
134        assert_eq!(out, vec![("60", CondUnit::Star, TimeUnit::Second)]);
135    }
136
137    #[test]
138    fn test_cond_time2() {
139        let (input, out) = cond_time.parse_peek(" * 60*30").unwrap();
140        assert_eq!(input, "");
141        assert_eq!(
142            out,
143            vec![
144                ("60", CondUnit::Star, TimeUnit::Second),
145                ("30", CondUnit::Star, TimeUnit::Second),
146            ]
147        );
148    }
149
150    #[test]
151    fn test_duration_parse0() {
152        let duration = parse("0").unwrap();
153        assert_eq!(duration, Duration::new(0, 0));
154
155        let duration = parse("0    ").unwrap();
156        assert_eq!(duration, Duration::new(0, 0));
157
158        let duration = parse("     0    ").unwrap();
159        assert_eq!(duration, Duration::new(0, 0));
160
161        let duration = parse("1").unwrap();
162        assert_eq!(duration, Duration::new(1, 0));
163
164        let duration = parse("0m").unwrap();
165        assert_eq!(duration, Duration::new(0, 0));
166
167        let duration = parse("1hr").unwrap();
168        assert_eq!(duration, Duration::new(3600, 0));
169
170        let duration = parse("1m31").unwrap();
171        assert_eq!(duration, Duration::new(91, 0));
172
173        let duration = parse("1m31s").unwrap();
174        assert_eq!(duration, Duration::new(91, 0));
175
176        #[cfg(not(feature = "no_calc"))]
177        {
178            let duration = parse("1m+31").unwrap();
179            assert_eq!(duration, Duration::new(91, 0));
180            let duration = parse("1m*60").unwrap();
181            assert_eq!(duration, Duration::new(3600, 0));
182
183            let duration = parse("1m*60*20").unwrap();
184            assert_eq!(duration, Duration::new(72000, 0));
185
186            let duration = parse("1m+60+24").unwrap();
187            assert_eq!(duration, Duration::new(144, 0));
188
189            let duration = parse("1m+60+24 ").unwrap();
190            assert_eq!(duration, Duration::new(144, 0));
191
192            let duration = parse("      1m      +  60 +             24 ").unwrap();
193            assert_eq!(duration, Duration::new(144, 0))
194        }
195    }
196
197    #[cfg(feature = "cn_unit")]
198    #[test]
199    fn test_parse_unit_cn() {
200        let duration = parse("1年").unwrap();
201        assert_eq!(duration, Duration::new(31536000, 0));
202
203        let duration = parse("1月").unwrap();
204        assert_eq!(duration, Duration::new(2592000, 0));
205
206        let duration = parse("1周").unwrap();
207        assert_eq!(duration, Duration::new(604800, 0));
208
209        let duration = parse("1日").unwrap();
210        assert_eq!(duration, Duration::new(86400, 0));
211
212        let duration = parse("1天").unwrap();
213        assert_eq!(duration, Duration::new(86400, 0));
214
215        let duration = parse("1时").unwrap();
216        assert_eq!(duration, Duration::new(3600, 0));
217
218        let duration = parse("1分").unwrap();
219        assert_eq!(duration, Duration::new(60, 0));
220
221        let duration = parse("1秒").unwrap();
222        assert_eq!(duration, Duration::new(1, 0));
223
224        let duration = parse("1毫秒").unwrap();
225        assert_eq!(duration, Duration::new(0, 1 * 1000 * 1000));
226
227        let duration = parse("1微秒").unwrap();
228        assert_eq!(duration, Duration::new(0, 1 * 1000));
229
230        let duration = parse("1纳秒").unwrap();
231        assert_eq!(duration, Duration::new(0, 1));
232
233        let duration = parse("1年 2日").unwrap();
234        assert_eq!(duration, Duration::new(31708800, 0));
235
236        let duration = parse("1分31秒").unwrap();
237        assert_eq!(duration, Duration::new(91, 0));
238
239        #[cfg(not(feature = "no_calc"))]
240        {
241            let duration = parse("1分+31秒").unwrap();
242            assert_eq!(duration, Duration::new(91, 0));
243
244            let duration = parse("1分+31秒+2毫秒+3纳秒").unwrap();
245            assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
246
247            let duration = parse(" 1分+   31秒 + 2毫秒+  3纳秒  ").unwrap();
248            assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
249        }
250    }
251
252    #[test]
253    fn test_duration_err() {
254        #[cfg(not(feature = "no_calc"))]
255        assert_eq!(
256            catch_err!(parse("0m+3-5")),
257            r#"
2580m+3-5
259    ^
260expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
261                .trim()
262        );
263        #[cfg(feature = "no_calc")]
264        assert_eq!(
265            catch_err!(parse("0m3-5")),
266            r#"
2670m3-5
268   ^
269expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
270                .trim()
271        );
272
273        assert_eq!(
274            catch_err!(parse("0mxyz")),
275            r#"
2760mxyz
277 ^
278expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
279                .trim()
280        );
281
282        #[cfg(not(feature = "no_calc"))]
283        assert_eq!(
284            catch_err!(parse("3ms-2ms")),
285            r#"
2863ms-2ms
287   ^
288expected ['+', '*']"#
289                .trim()
290        );
291
292        assert_eq!(catch_err!(parse("")), "Empty input");
293    }
294
295    #[test]
296    fn test_parse() {
297        let duration = parse("1d").unwrap();
298        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
299
300        let duration = parse("   1d").unwrap();
301        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
302
303        let duration = parse("1d   ").unwrap();
304        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
305
306        let duration = parse("   1d   ").unwrap();
307        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
308
309        #[cfg(not(feature = "no_calc"))]
310        {
311            let duration = parse("3m+31").unwrap(); //the default duration unit is second.
312            assert_eq!(duration, Duration::new(211, 0));
313
314            let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
315            assert_eq!(duration, Duration::new(211, 0));
316
317            let duration = parse("3m + 13s + 29ms").unwrap();
318            assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
319
320            let duration = parse("3m + 1s + 29ms +17µs").unwrap();
321            assert_eq!(
322                duration,
323                Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
324            );
325
326            let duration = parse("1m*10").unwrap(); //the default duration unit is second.
327            assert_eq!(duration, Duration::new(600, 0));
328
329            let duration = parse("1m*10ms").unwrap();
330            assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
331
332            let duration = parse("1m * 1ns").unwrap();
333            assert_eq!(duration, Duration::new(0, 60));
334
335            let duration = parse("1m * 1m").unwrap();
336            assert_eq!(duration, Duration::new(3600, 0));
337
338            let duration = parse("3m + 31").unwrap();
339            assert_eq!(duration, Duration::new(211, 0));
340        }
341
342        #[cfg(feature = "no_calc")]
343        {
344            let duration = parse("3m31").unwrap(); //the default duration unit is second.
345            assert_eq!(duration, Duration::new(211, 0));
346
347            let duration = parse("3m  31").unwrap(); //the default duration unit is second.
348            assert_eq!(duration, Duration::new(211, 0));
349        }
350
351        let duration = parse("3m  31s").unwrap();
352        assert_eq!(duration, Duration::new(211, 0));
353
354        let duration = parse("3m31s0ns").unwrap();
355        assert_eq!(duration, Duration::new(211, 0));
356
357        let duration = parse("  3m 31s 0ns ").unwrap();
358        assert_eq!(duration, Duration::new(211, 0));
359
360        let duration = parse("1d2h3min4s").unwrap();
361        assert_eq!(duration, Duration::new(93784, 0));
362    }
363
364    #[cfg(not(feature = "no_calc"))]
365    #[test]
366    fn test_overflow_plus() {
367        assert_eq!(
368            catch_err!(parse("10000000000000000y+60")),
369            r#"
37010000000000000000y+60
371^
372overflow error"#
373                .trim()
374                .to_string()
375        );
376    }
377
378    #[cfg(not(feature = "no_calc"))]
379    #[test]
380    fn test_max_mul() {
381        let duration = parse("580y*1").unwrap();
382        assert_eq!(
383            duration,
384            std::time::Duration::from_millis(18290880000) * 1000
385        );
386    }
387
388    #[cfg(not(feature = "no_calc"))]
389    #[test]
390    fn test_overflow_mul() {
391        let err = parse("580y*2").err().unwrap();
392        assert_eq!(err, "overflow error");
393    }
394
395    #[test]
396    fn test_parse_optional_spaces() {
397        let duration = parse("1 d").unwrap();
398        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
399
400        #[cfg(not(feature = "no_calc"))]
401        {
402            let duration = parse("3 m+31").unwrap(); //the default duration unit is second.
403            assert_eq!(duration, Duration::new(211, 0));
404
405            let duration = parse("3 m + 31").unwrap(); //the default duration unit is second.
406            assert_eq!(duration, Duration::new(211, 0));
407
408            let duration = parse("3 m + 13 s + 29 ms").unwrap();
409            assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
410
411            let duration = parse("3 m + 1 s + 29 ms +17µs").unwrap();
412            assert_eq!(
413                duration,
414                Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
415            );
416
417            let duration = parse("1 m*10").unwrap(); //the default duration unit is second.
418            assert_eq!(duration, Duration::new(600, 0));
419
420            let duration = parse("1 m*10 ms").unwrap();
421            assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
422
423            let duration = parse("1 m * 1ns").unwrap();
424            assert_eq!(duration, Duration::new(0, 60));
425
426            let duration = parse("1 m * 1 m").unwrap();
427            assert_eq!(duration, Duration::new(3600, 0));
428
429            let duration = parse("3 m + 31").unwrap();
430            assert_eq!(duration, Duration::new(211, 0));
431        }
432
433        #[cfg(feature = "no_calc")]
434        {
435            let duration = parse("3 m31").unwrap(); //the default duration unit is second.
436            assert_eq!(duration, Duration::new(211, 0));
437        }
438
439        let duration = parse("3 m  31 s").unwrap();
440        assert_eq!(duration, Duration::new(211, 0));
441
442        let duration = parse("3 m31 s0 ns").unwrap();
443        assert_eq!(duration, Duration::new(211, 0));
444
445        let duration = parse("  3 m 31 s 0 ns ").unwrap();
446        assert_eq!(duration, Duration::new(211, 0));
447
448        let duration = parse("1 d2 h3 min 4s").unwrap();
449        assert_eq!(duration, Duration::new(93784, 0));
450    }
451}
452
453#[cfg(all(test, feature = "chrono"))]
454mod chrono_tests {
455    use crate::{
456        after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
457        parse_chrono,
458    };
459    use chrono::{Datelike, Utc};
460
461    #[test]
462    fn test_parse_chrono() {
463        use chrono::Duration;
464        #[cfg(not(feature = "no_calc"))]
465        let duration = parse_chrono("1m+60+24 ").unwrap();
466        #[cfg(feature = "no_calc")]
467        let duration = parse_chrono("1m84s ").unwrap();
468        assert_eq!(duration, Duration::seconds(144))
469    }
470
471    #[test]
472    fn test_after_naive_date_time() {
473        let date = Utc::now().naive_utc().date();
474        let jd = date.num_days_from_ce() + 180;
475        let date = after_naive_date_time("180d").unwrap();
476        assert_eq!(date.num_days_from_ce(), jd)
477    }
478
479    #[test]
480    fn test_after_naive_date() {
481        let date = Utc::now().naive_utc().date();
482        let jd = date.num_days_from_ce() + 180;
483        let date = after_naive_date("180d").unwrap();
484        assert_eq!(date.num_days_from_ce(), jd)
485    }
486
487    #[test]
488    fn test_before_naive_date_time() {
489        let date = Utc::now().naive_utc().date();
490        let jd = date.num_days_from_ce() - 180;
491        let date = before_naive_date_time("180d").unwrap();
492        assert_eq!(date.num_days_from_ce(), jd)
493    }
494
495    #[test]
496    fn test_before_naive_date() {
497        let date = Utc::now().naive_utc().date();
498        let jd = date.num_days_from_ce() - 180;
499        let date = before_naive_date("180d").unwrap();
500        assert_eq!(date.num_days_from_ce(), jd)
501    }
502}
503
504#[cfg(all(test, feature = "time"))]
505mod time_tests {
506    use crate::parse_time;
507    use time::Duration;
508
509    #[test]
510    fn test_parse_time() {
511        #[cfg(not(feature = "no_calc"))]
512        {
513            let duration = parse_time("1m+60+24 ").unwrap();
514            assert_eq!(duration, Duration::seconds(144))
515        }
516        #[cfg(feature = "no_calc")]
517        {
518            let duration = parse_time("1m84 ").unwrap();
519            assert_eq!(duration, Duration::seconds(144))
520        }
521    }
522}