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    #[cfg(all(feature = "no_calc", not(feature = "calc")))]
74    {
75        use crate::DError;
76
77        let d = repeat(0.., parse_expr_time)
78            .try_fold(
79                Default::default,
80                |mut acc: u64, item| -> Result<_, DError> {
81                    acc = acc.checked_add(item).ok_or(DError::OverflowError)?;
82                    Ok(acc)
83                },
84            )
85            .parse(input)
86            .map_err(|err| err.to_string())?;
87        return Ok(Duration::from_nanos(d));
88    }
89
90    #[cfg(feature = "calc")]
91    {
92        let (unit_time, cond_val) = (parse_expr_time, cond_time)
93            .parse(input)
94            .map_err(|e| format!("{}", e))?;
95
96        let (init_cond, init_duration) = if cond_val.is_empty() {
97            CondUnit::init()
98        } else {
99            cond_val.calc().map_err(|err| err.to_string())?
100        };
101
102        let duration = init_cond
103            .calc(unit_time, init_duration)
104            .map_err(|err| err.to_string())?;
105        Ok(duration)
106    }
107}
108
109#[cfg(test)]
110#[allow(clippy::identity_op)]
111mod tests {
112    use super::*;
113    use crate::{catch_err, unit::TimeUnit, CondUnit};
114
115    #[test]
116    fn test_parse_expr_time() {
117        let (input, val) = parse_expr_time.parse_peek("123m").unwrap();
118        assert_eq!(input, "");
119        assert_eq!(val, 7380000000000);
120    }
121
122    #[test]
123    fn test_cond_unit() {
124        let (input, format) = cond_unit1.parse_peek("*123").unwrap();
125        assert_eq!(input, "123");
126        assert_eq!(format, CondUnit::Star);
127    }
128
129    #[test]
130    fn test_cond_time() {
131        let (input, out) = cond_time.parse_peek(" * 60").unwrap();
132        assert_eq!(input, "");
133        assert_eq!(out, vec![("60", CondUnit::Star, TimeUnit::Second)]);
134    }
135
136    #[test]
137    fn test_cond_time2() {
138        let (input, out) = cond_time.parse_peek(" * 60*30").unwrap();
139        assert_eq!(input, "");
140        assert_eq!(
141            out,
142            vec![
143                ("60", CondUnit::Star, TimeUnit::Second),
144                ("30", CondUnit::Star, TimeUnit::Second),
145            ]
146        );
147    }
148
149    #[test]
150    fn test_duration_parse0() {
151        let duration = parse("0").unwrap();
152        assert_eq!(duration, Duration::new(0, 0));
153
154        let duration = parse("0    ").unwrap();
155        assert_eq!(duration, Duration::new(0, 0));
156
157        let duration = parse("     0    ").unwrap();
158        assert_eq!(duration, Duration::new(0, 0));
159
160        let duration = parse("1").unwrap();
161        assert_eq!(duration, Duration::new(1, 0));
162
163        let duration = parse("0m").unwrap();
164        assert_eq!(duration, Duration::new(0, 0));
165
166        let duration = parse("1hr").unwrap();
167        assert_eq!(duration, Duration::new(3600, 0));
168
169        let duration = parse("1m+31").unwrap();
170        assert_eq!(duration, Duration::new(91, 0));
171
172        let duration = parse("1m31").unwrap();
173        assert_eq!(duration, Duration::new(91, 0));
174
175        let duration = parse("1m31s").unwrap();
176        assert_eq!(duration, Duration::new(91, 0));
177
178        let duration = parse("1m*60").unwrap();
179        assert_eq!(duration, Duration::new(3600, 0));
180
181        let duration = parse("1m*60*20").unwrap();
182        assert_eq!(duration, Duration::new(72000, 0));
183
184        let duration = parse("1m+60+24").unwrap();
185        assert_eq!(duration, Duration::new(144, 0));
186
187        let duration = parse("1m+60+24 ").unwrap();
188        assert_eq!(duration, Duration::new(144, 0));
189
190        let duration = parse("      1m      +  60 +             24 ").unwrap();
191        assert_eq!(duration, Duration::new(144, 0))
192    }
193
194    #[cfg(feature = "cn_unit")]
195    #[test]
196    fn test_parse_unit_cn() {
197        let duration = parse("1年").unwrap();
198        assert_eq!(duration, Duration::new(31536000, 0));
199
200        let duration = parse("1月").unwrap();
201        assert_eq!(duration, Duration::new(2592000, 0));
202
203        let duration = parse("1周").unwrap();
204        assert_eq!(duration, Duration::new(604800, 0));
205
206        let duration = parse("1日").unwrap();
207        assert_eq!(duration, Duration::new(86400, 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(3600, 0));
214
215        let duration = parse("1分").unwrap();
216        assert_eq!(duration, Duration::new(60, 0));
217
218        let duration = parse("1秒").unwrap();
219        assert_eq!(duration, Duration::new(1, 0));
220
221        let duration = parse("1毫秒").unwrap();
222        assert_eq!(duration, Duration::new(0, 1 * 1000 * 1000));
223
224        let duration = parse("1微秒").unwrap();
225        assert_eq!(duration, Duration::new(0, 1 * 1000));
226
227        let duration = parse("1纳秒").unwrap();
228        assert_eq!(duration, Duration::new(0, 1));
229
230        let duration = parse("1年 2日").unwrap();
231        assert_eq!(duration, Duration::new(31708800, 0));
232
233        let duration = parse("1分31秒").unwrap();
234        assert_eq!(duration, Duration::new(91, 0));
235
236        let duration = parse("1分+31秒").unwrap();
237        assert_eq!(duration, Duration::new(91, 0));
238
239        let duration = parse("1分+31秒+2毫秒+3纳秒").unwrap();
240        assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
241
242        let duration = parse(" 1分+   31秒 + 2毫秒+  3纳秒  ").unwrap();
243        assert_eq!(duration, Duration::new(91, 2 * 1000 * 1000 + 3));
244    }
245
246    #[test]
247    fn test_duration_err() {
248        assert_eq!(
249            catch_err!(parse("0m+3-5")),
250            r#"
2510m+3-5
252    ^
253expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
254                .trim()
255        );
256
257        assert_eq!(
258            catch_err!(parse("0mxyz")),
259            r#"
2600mxyz
261 ^
262expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
263                .trim()
264        );
265
266        assert_eq!(
267            catch_err!(parse("3ms-2ms")),
268            r#"
2693ms-2ms
270   ^
271expected ['+', '*']"#
272                .trim()
273        );
274
275        assert_eq!(catch_err!(parse("")), "Empty input");
276    }
277
278    #[test]
279    fn test_parse() {
280        let duration = parse("1d").unwrap();
281        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
282
283        let duration = parse("   1d").unwrap();
284        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
285
286        let duration = parse("1d   ").unwrap();
287        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
288
289        let duration = parse("   1d   ").unwrap();
290        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
291
292        let duration = parse("3m+31").unwrap(); //the default duration unit is second.
293        assert_eq!(duration, Duration::new(211, 0));
294
295        let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
296        assert_eq!(duration, Duration::new(211, 0));
297
298        let duration = parse("3m + 13s + 29ms").unwrap();
299        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
300
301        let duration = parse("3m + 1s + 29ms +17µs").unwrap();
302        assert_eq!(
303            duration,
304            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
305        );
306
307        let duration = parse("1m*10").unwrap(); //the default duration unit is second.
308        assert_eq!(duration, Duration::new(600, 0));
309
310        let duration = parse("1m*10ms").unwrap();
311        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
312
313        let duration = parse("1m * 1ns").unwrap();
314        assert_eq!(duration, Duration::new(0, 60));
315
316        let duration = parse("1m * 1m").unwrap();
317        assert_eq!(duration, Duration::new(3600, 0));
318
319        let duration = parse("3m + 31").unwrap();
320        assert_eq!(duration, Duration::new(211, 0));
321
322        let duration = parse("3m  31s").unwrap();
323        assert_eq!(duration, Duration::new(211, 0));
324
325        let duration = parse("3m31s0ns").unwrap();
326        assert_eq!(duration, Duration::new(211, 0));
327
328        let duration = parse("  3m 31s 0ns ").unwrap();
329        assert_eq!(duration, Duration::new(211, 0));
330
331        let duration = parse("1d2h3min4s").unwrap();
332        assert_eq!(duration, Duration::new(93784, 0));
333    }
334
335    #[test]
336    fn test_overflow_plus() {
337        assert_eq!(
338            catch_err!(parse("10000000000000000y+60")),
339            r#"
34010000000000000000y+60
341^
342overflow error"#
343                .trim()
344                .to_string()
345        );
346    }
347
348    #[test]
349    fn test_max_mul() {
350        let duration = parse("580y*1").unwrap();
351        assert_eq!(
352            duration,
353            std::time::Duration::from_millis(18290880000) * 1000
354        );
355    }
356
357    #[test]
358    fn test_overflow_mul() {
359        let err = parse("580y*2").err().unwrap();
360        assert_eq!(err, "overflow error");
361    }
362
363    #[test]
364    fn test_parse_optional_spaces() {
365        let duration = parse("1 d").unwrap();
366        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
367
368        let duration = parse("3 m+31").unwrap(); //the default duration unit is second.
369        assert_eq!(duration, Duration::new(211, 0));
370
371        let duration = parse("3 m + 31").unwrap(); //the default duration unit is second.
372        assert_eq!(duration, Duration::new(211, 0));
373
374        let duration = parse("3 m + 13 s + 29 ms").unwrap();
375        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
376
377        let duration = parse("3 m + 1 s + 29 ms +17µs").unwrap();
378        assert_eq!(
379            duration,
380            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
381        );
382
383        let duration = parse("1 m*10").unwrap(); //the default duration unit is second.
384        assert_eq!(duration, Duration::new(600, 0));
385
386        let duration = parse("1 m*10 ms").unwrap();
387        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
388
389        let duration = parse("1 m * 1ns").unwrap();
390        assert_eq!(duration, Duration::new(0, 60));
391
392        let duration = parse("1 m * 1 m").unwrap();
393        assert_eq!(duration, Duration::new(3600, 0));
394
395        let duration = parse("3 m + 31").unwrap();
396        assert_eq!(duration, Duration::new(211, 0));
397
398        let duration = parse("3 m  31 s").unwrap();
399        assert_eq!(duration, Duration::new(211, 0));
400
401        let duration = parse("3 m31 s0 ns").unwrap();
402        assert_eq!(duration, Duration::new(211, 0));
403
404        let duration = parse("  3 m 31 s 0 ns ").unwrap();
405        assert_eq!(duration, Duration::new(211, 0));
406
407        let duration = parse("1 d2 h3 min 4s").unwrap();
408        assert_eq!(duration, Duration::new(93784, 0));
409    }
410}
411
412#[cfg(all(test, feature = "chrono"))]
413mod chrono_tests {
414    use crate::{
415        after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
416        parse_chrono,
417    };
418    use chrono::{Datelike, Utc};
419
420    #[test]
421    fn test_parse_chrono() {
422        use chrono::Duration;
423        let duration = parse_chrono("1m+60+24 ").unwrap();
424        assert_eq!(duration, Duration::seconds(144))
425    }
426
427    #[test]
428    fn test_after_naive_date_time() {
429        let date = Utc::now().naive_utc().date();
430        let jd = date.num_days_from_ce() + 180;
431        let date = after_naive_date_time("180d").unwrap();
432        assert_eq!(date.num_days_from_ce(), jd)
433    }
434
435    #[test]
436    fn test_after_naive_date() {
437        let date = Utc::now().naive_utc().date();
438        let jd = date.num_days_from_ce() + 180;
439        let date = after_naive_date("180d").unwrap();
440        assert_eq!(date.num_days_from_ce(), jd)
441    }
442
443    #[test]
444    fn test_before_naive_date_time() {
445        let date = Utc::now().naive_utc().date();
446        let jd = date.num_days_from_ce() - 180;
447        let date = before_naive_date_time("180d").unwrap();
448        assert_eq!(date.num_days_from_ce(), jd)
449    }
450
451    #[test]
452    fn test_before_naive_date() {
453        let date = Utc::now().naive_utc().date();
454        let jd = date.num_days_from_ce() - 180;
455        let date = before_naive_date("180d").unwrap();
456        assert_eq!(date.num_days_from_ce(), jd)
457    }
458}
459
460#[cfg(all(test, feature = "time"))]
461mod time_tests {
462    use crate::parse_time;
463    use time::Duration;
464
465    #[test]
466    fn test_parse_time() {
467        let duration = parse_time("1m+60+24 ").unwrap();
468        assert_eq!(duration, Duration::seconds(144))
469    }
470}