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    #[test]
195    fn test_duration_err() {
196        assert_eq!(
197            catch_err!(parse("0m+3-5")),
198            r#"
1990m+3-5
200    ^
201expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
202                .trim()
203        );
204
205        assert_eq!(
206            catch_err!(parse("0mxyz")),
207            r#"
2080mxyz
209 ^
210expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#
211                .trim()
212        );
213
214        assert_eq!(
215            catch_err!(parse("3ms-2ms")),
216            r#"
2173ms-2ms
218   ^
219expected ['+', '*']"#
220                .trim()
221        );
222
223        assert_eq!(catch_err!(parse("")), "Empty input");
224    }
225
226    #[test]
227    fn test_parse() {
228        let duration = parse("1d").unwrap();
229        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
230
231        let duration = parse("   1d").unwrap();
232        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
233
234        let duration = parse("1d   ").unwrap();
235        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
236
237        let duration = parse("   1d   ").unwrap();
238        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
239
240        let duration = parse("3m+31").unwrap(); //the default duration unit is second.
241        assert_eq!(duration, Duration::new(211, 0));
242
243        let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
244        assert_eq!(duration, Duration::new(211, 0));
245
246        let duration = parse("3m + 13s + 29ms").unwrap();
247        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
248
249        let duration = parse("3m + 1s + 29ms +17µs").unwrap();
250        assert_eq!(
251            duration,
252            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
253        );
254
255        let duration = parse("1m*10").unwrap(); //the default duration unit is second.
256        assert_eq!(duration, Duration::new(600, 0));
257
258        let duration = parse("1m*10ms").unwrap();
259        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
260
261        let duration = parse("1m * 1ns").unwrap();
262        assert_eq!(duration, Duration::new(0, 60));
263
264        let duration = parse("1m * 1m").unwrap();
265        assert_eq!(duration, Duration::new(3600, 0));
266
267        let duration = parse("3m + 31").unwrap();
268        assert_eq!(duration, Duration::new(211, 0));
269
270        let duration = parse("3m  31s").unwrap();
271        assert_eq!(duration, Duration::new(211, 0));
272
273        let duration = parse("3m31s0ns").unwrap();
274        assert_eq!(duration, Duration::new(211, 0));
275
276        let duration = parse("  3m 31s 0ns ").unwrap();
277        assert_eq!(duration, Duration::new(211, 0));
278
279        let duration = parse("1d2h3min4s").unwrap();
280        assert_eq!(duration, Duration::new(93784, 0));
281    }
282
283    #[test]
284    fn test_overflow_plus() {
285        assert_eq!(
286            catch_err!(parse("10000000000000000y+60")),
287            r#"
28810000000000000000y+60
289^
290overflow error"#
291                .trim()
292                .to_string()
293        );
294    }
295
296    #[test]
297    fn test_max_mul() {
298        let duration = parse("580y*1").unwrap();
299        assert_eq!(
300            duration,
301            std::time::Duration::from_millis(18290880000) * 1000
302        );
303    }
304
305    #[test]
306    fn test_overflow_mul() {
307        let err = parse("580y*2").err().unwrap();
308        assert_eq!(err, "overflow error");
309    }
310
311    #[test]
312    fn test_parse_optional_spaces() {
313        let duration = parse("1 d").unwrap();
314        assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
315
316        let duration = parse("3 m+31").unwrap(); //the default duration unit is second.
317        assert_eq!(duration, Duration::new(211, 0));
318
319        let duration = parse("3 m + 31").unwrap(); //the default duration unit is second.
320        assert_eq!(duration, Duration::new(211, 0));
321
322        let duration = parse("3 m + 13 s + 29 ms").unwrap();
323        assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
324
325        let duration = parse("3 m + 1 s + 29 ms +17µs").unwrap();
326        assert_eq!(
327            duration,
328            Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
329        );
330
331        let duration = parse("1 m*10").unwrap(); //the default duration unit is second.
332        assert_eq!(duration, Duration::new(600, 0));
333
334        let duration = parse("1 m*10 ms").unwrap();
335        assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
336
337        let duration = parse("1 m * 1ns").unwrap();
338        assert_eq!(duration, Duration::new(0, 60));
339
340        let duration = parse("1 m * 1 m").unwrap();
341        assert_eq!(duration, Duration::new(3600, 0));
342
343        let duration = parse("3 m + 31").unwrap();
344        assert_eq!(duration, Duration::new(211, 0));
345
346        let duration = parse("3 m  31 s").unwrap();
347        assert_eq!(duration, Duration::new(211, 0));
348
349        let duration = parse("3 m31 s0 ns").unwrap();
350        assert_eq!(duration, Duration::new(211, 0));
351
352        let duration = parse("  3 m 31 s 0 ns ").unwrap();
353        assert_eq!(duration, Duration::new(211, 0));
354
355        let duration = parse("1 d2 h3 min 4s").unwrap();
356        assert_eq!(duration, Duration::new(93784, 0));
357    }
358}
359
360#[cfg(all(test, feature = "chrono"))]
361mod chrono_tests {
362    use crate::{
363        after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
364        parse_chrono,
365    };
366    use chrono::{Datelike, Utc};
367
368    #[test]
369    fn test_parse_chrono() {
370        use chrono::Duration;
371        let duration = parse_chrono("1m+60+24 ").unwrap();
372        assert_eq!(duration, Duration::seconds(144))
373    }
374
375    #[test]
376    fn test_after_naive_date_time() {
377        let date = Utc::now().naive_utc().date();
378        let jd = date.num_days_from_ce() + 180;
379        let date = after_naive_date_time("180d").unwrap();
380        assert_eq!(date.num_days_from_ce(), jd)
381    }
382
383    #[test]
384    fn test_after_naive_date() {
385        let date = Utc::now().naive_utc().date();
386        let jd = date.num_days_from_ce() + 180;
387        let date = after_naive_date("180d").unwrap();
388        assert_eq!(date.num_days_from_ce(), jd)
389    }
390
391    #[test]
392    fn test_before_naive_date_time() {
393        let date = Utc::now().naive_utc().date();
394        let jd = date.num_days_from_ce() - 180;
395        let date = before_naive_date_time("180d").unwrap();
396        assert_eq!(date.num_days_from_ce(), jd)
397    }
398
399    #[test]
400    fn test_before_naive_date() {
401        let date = Utc::now().naive_utc().date();
402        let jd = date.num_days_from_ce() - 180;
403        let date = before_naive_date("180d").unwrap();
404        assert_eq!(date.num_days_from_ce(), jd)
405    }
406}
407
408#[cfg(all(test, feature = "time"))]
409mod time_tests {
410    use crate::parse_time;
411    use time::Duration;
412
413    #[test]
414    fn test_parse_time() {
415        let duration = parse_time("1m+60+24 ").unwrap();
416        assert_eq!(duration, Duration::seconds(144))
417    }
418}