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