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 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 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(); assert_eq!(duration, Duration::new(211, 0));
218
219 let duration = parse("3m + 31").unwrap(); 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(); 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(); assert_eq!(duration, Duration::new(211, 0));
294
295 let duration = parse("3 m + 31").unwrap(); 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(); 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}