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 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 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(); assert_eq!(duration, Duration::new(211, 0));
242
243 let duration = parse("3m + 31").unwrap(); 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(); 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(); assert_eq!(duration, Duration::new(211, 0));
318
319 let duration = parse("3 m + 31").unwrap(); 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(); 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}