duration_str/
parser.rs

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