1use nom::branch::alt;
2use nom::bytes::complete::tag;
3use nom::character::complete::{alpha1, digit1, multispace0};
4use nom::combinator::{all_consuming, eof, map, map_res, opt};
5use nom::multi::separated_list1;
6use nom::sequence::{delimited, separated_pair, terminated, tuple, pair, preceded};
7use nom::IResult;
8
9use std::convert::TryFrom;
10use std::str::{self, FromStr};
11
12use crate::error::{Error, ErrorKind};
13use crate::schedule::{ScheduleFields, Schedule};
14use crate::specifier::{RootSpecifier, Specifier};
15use crate::time_unit::*;
16use crate::ordinal::*;
17
18impl FromStr for Schedule {
19 type Err = Error;
20 fn from_str(expression: &str) -> Result<Self, Self::Err> {
21 match schedule(expression) {
22 Ok((_, schedule_fields)) => {
23 Ok(Schedule::new(String::from(expression), schedule_fields))
24 } Err(e) => {
26 println!("异常信息:{:?}", e);
27 Err(ErrorKind::Expression("Invalid cron expression.".to_owned()).into())
28 }, }
30 }
31}
32impl TryFrom<&str> for Schedule {
33 type Error = Error;
34
35 fn try_from(value: &str) -> Result<Self, Self::Error> {
36 Self::from_str(value)
37 }
38}
39
40#[derive(Debug, PartialEq)]
41pub struct Field {
42 pub specifiers: Vec<RootSpecifier>, }
44
45trait FromField
46where
47 Self: Sized,
48{
49 fn from_field(field: Field) -> Result<Self, Error>;
51}
52
53impl<T> FromField for T
54where
55 T: TimeUnitField,
56{
57 fn from_field(field: Field) -> Result<T, Error> {
58 if field.specifiers.len() == 1 &&
59 field.specifiers.get(0).unwrap() == &RootSpecifier::from(Specifier::All)
60 { return Ok(T::all()); }
61 let mut ordinals = OrdinalSet::new();
62 let mut matching_pattern: Pattern = "value".into();
63 let mut ordinal_list = OrdinalList::new();
64 for specifier in field.specifiers {
65 let (specifier_ordinals, pattern, ol): (OrdinalSet, Pattern, OrdinalList) = T::ordinals_from_root_specifier(&specifier)?;
66 matching_pattern = pattern;
67 ordinal_list = ol;
68 for ordinal in specifier_ordinals {
69 ordinals.insert(T::validate_ordinal(ordinal)?);
70 }
71 }
72 Ok(T::from_ordinal_set(ordinals, matching_pattern, ordinal_list))
73 }
74}
75
76fn ordinal(i: &str) -> IResult<&str, u32> {
77 map_res(delimited(multispace0, digit1, multispace0), u32::from_str)(i)
78}
79
80fn strict_ordinal(i: &str) -> IResult<&str, u32> {
81 map_res(preceded(multispace0, digit1), u32::from_str)(i)
82}
83
84fn name(i: &str) -> IResult<&str, String> {
85 map(
86 delimited(multispace0, alpha1, multispace0),
87 ToOwned::to_owned,
88 )(i)
89}
90
91fn point(i: &str) -> IResult<&str, Specifier> {
92 let (i, o) = ordinal(i)?;
93 Ok((i, Specifier::Point(o)))
94}
95
96fn named_point(i: &str) -> IResult<&str, RootSpecifier> {
97 let (i, n) = name(i)?;
98 Ok((i, RootSpecifier::NamedPoint(n)))
99}
100
101fn period(i: &str) -> IResult<&str, RootSpecifier> {
102 map(
103 separated_pair(specifier, tag("/"), ordinal),
104 |(start, step)| RootSpecifier::Period(start, step),
105 )(i)
106}
107
108fn period_with_any(i: &str) -> IResult<&str, RootSpecifier> {
109 map(
110 separated_pair(specifier_with_any, tag("/"), ordinal),
111 |(start, step)| RootSpecifier::Period(start, step),
112 )(i)
113}
114
115fn range(i: &str) -> IResult<&str, Specifier> {
116 map(
117 separated_pair(ordinal, tag("-"), ordinal),
118 |(start, end)| Specifier::Range(start, end),
119 )(i)
120}
121
122fn daymonth_last_day_name(i: &str) -> IResult<&str, String> {
123 map(
124 delimited(multispace0, tag("L"), multispace0),
125 ToOwned::to_owned,
126 )(i)
127}
128
129fn month_last_day(i: &str) -> IResult<&str, Specifier> {
130 let (i, n) = daymonth_last_day_name(i)?;
131 Ok((i, Specifier::MonthLast(n)))
132}
133
134fn month_last(i: &str) -> IResult<&str, Specifier> {
135 map(
136 pair(strict_ordinal, tag("L")),
137 |(num, pattern)| {
138 Specifier::MonthLastWithNum(num, pattern.to_string())
139 })(i)
140}
141
142fn month_last_word_day(i: &str) -> IResult<&str, Specifier> {
143 map(
144 pair(strict_ordinal, tag("W")),
145 |(start, pattern)| {
146 Specifier::MonthLastWithNum(start, pattern.to_string())
147 })(i)
148}
149
150fn named_range(i: &str) -> IResult<&str, Specifier> {
151 map(separated_pair(name, tag("-"), name), |(start, end)| {
152 Specifier::NamedRange(start, end)
153 })(i)
154}
155
156fn month_week(i: &str) -> IResult<&str, Specifier> {
157 map(separated_pair(ordinal, tag("#"), ordinal), |(week, week_num)| {
158 Specifier::MonthWeek(week, week_num, "#".to_string())
159 })(i)
160}
161
162fn all(i: &str) -> IResult<&str, Specifier> {
163 let (i, _) = tag("*")(i)?;
164 Ok((i, Specifier::All))
165}
166
167fn any(i: &str) -> IResult<&str, Specifier> {
168 let res = tag("?")(i);
169 match res {
170 Ok((rem, _)) => {
171 Ok((rem, Specifier::All))
172 },
173 Err(e) => {
174 Err(e)
175 }
176 }
177}
178
179fn specifier(i: &str) -> IResult<&str, Specifier> {
180 alt((all, range, month_last_word_day, month_last_day, month_last, month_week, point, named_range))(i)
181}
182
183fn specifier_with_any(i: &str) -> IResult<&str, Specifier> {
184 alt((any, specifier))(i)
185}
186
187fn root_specifier(i: &str) -> IResult<&str, RootSpecifier> {
188 alt((period, map(specifier, RootSpecifier::from), named_point))(i)
189}
190
191fn root_specifier_with_any(i: &str) -> IResult<&str, RootSpecifier> {
192 alt((
193 period_with_any,
194 map(specifier_with_any, RootSpecifier::from),
195 named_point,
196 ))(i)
197}
198
199fn root_specifier_list(i: &str) -> IResult<&str, Vec<RootSpecifier>> {
200 let list = separated_list1(tag(","), root_specifier);
201 let single_item = map(root_specifier, |spec| vec![spec]);
202 delimited(multispace0, alt((list, single_item)), multispace0)(i)
203}
204
205fn root_specifier_list_with_any(i: &str) -> IResult<&str, Vec<RootSpecifier>> {
206 let list = separated_list1(tag(","), root_specifier_with_any);
207 let single_item = map(root_specifier_with_any, |spec| vec![spec]);
208 delimited(multispace0, alt((list, single_item)), multispace0)(i)
209}
210
211fn field(i: &str) -> IResult<&str, Field> {
212 let (i, specifiers) = root_specifier_list(i)?;
213 Ok((i, Field { specifiers }))
214}
215
216fn field_with_any(i: &str) -> IResult<&str, Field> {
217 let (i, specifiers) = root_specifier_list_with_any(i)?;
218 Ok((i, Field { specifiers }))
219}
220
221fn shorthand_yearly(i: &str) -> IResult<&str, ScheduleFields> {
222 let (i, _) = tag("@yearly")(i)?;
223 let fields = ScheduleFields::new(
224 Seconds::from_ordinal(0, "value".into(), OrdinalList::new()),
225 Minutes::from_ordinal(0, "value".into(), OrdinalList::new()),
226 Hours::from_ordinal(0, "value".into(), OrdinalList::new()),
227 DaysOfMonth::from_ordinal(1, "value".into(), OrdinalList::new()),
228 Months::from_ordinal(1, "value".into(), OrdinalList::new()),
229 DaysOfWeek::all(),
230 Years::all(),
231 );
232 Ok((i, fields))
233}
234
235fn shorthand_monthly(i: &str) -> IResult<&str, ScheduleFields> {
236 let (i, _) = tag("@monthly")(i)?;
237 let fields = ScheduleFields::new(
238 Seconds::from_ordinal(0, "value".into(), OrdinalList::new()),
239 Minutes::from_ordinal(0, "value".into(), OrdinalList::new()),
240 Hours::from_ordinal(0, "value".into(), OrdinalList::new()),
241 DaysOfMonth::from_ordinal(1, "value".into(), OrdinalList::new()),
242 Months::all(),
243 DaysOfWeek::all(),
244 Years::all(),
245 );
246 Ok((i, fields))
247}
248
249fn shorthand_weekly(i: &str) -> IResult<&str, ScheduleFields> {
250 let (i, _) = tag("@weekly")(i)?;
251 let fields = ScheduleFields::new(
252 Seconds::from_ordinal(0, "value".into(), OrdinalList::new()),
253 Minutes::from_ordinal(0, "value".into(), OrdinalList::new()),
254 Hours::from_ordinal(0, "value".into(), OrdinalList::new()),
255 DaysOfMonth::all(),
256 Months::all(),
257 DaysOfWeek::from_ordinal(1, "value".into(), OrdinalList::new()),
258 Years::all(),
259 );
260 Ok((i, fields))
261}
262
263fn shorthand_daily(i: &str) -> IResult<&str, ScheduleFields> {
264 let (i, _) = tag("@daily")(i)?;
265 let fields = ScheduleFields::new(
266 Seconds::from_ordinal(0, "value".into(), OrdinalList::new()),
267 Minutes::from_ordinal(0, "value".into(), OrdinalList::new()),
268 Hours::from_ordinal(0, "value".into(), OrdinalList::new()),
269 DaysOfMonth::all(),
270 Months::all(),
271 DaysOfWeek::all(),
272 Years::all(),
273 );
274 Ok((i, fields))
275}
276
277fn shorthand_hourly(i: &str) -> IResult<&str, ScheduleFields> {
278 let (i, _) = tag("@hourly")(i)?;
279 let fields = ScheduleFields::new(
280 Seconds::from_ordinal(0, "value".into(), OrdinalList::new()),
281 Minutes::from_ordinal(0, "value".into(), OrdinalList::new()),
282 Hours::all(),
283 DaysOfMonth::all(),
284 Months::all(),
285 DaysOfWeek::all(),
286 Years::all(),
287 );
288 Ok((i, fields))
289}
290
291fn shorthand(i: &str) -> IResult<&str, ScheduleFields> {
292 let keywords = alt((
293 shorthand_yearly,
294 shorthand_monthly,
295 shorthand_weekly,
296 shorthand_daily,
297 shorthand_hourly,
298 ));
299 delimited(multispace0, keywords, multispace0)(i)
300}
301
302fn longhand(i: &str) -> IResult<&str, ScheduleFields> {
303 let seconds = map_res(field, Seconds::from_field);
304 let minutes = map_res(field, Minutes::from_field);
305 let hours = map_res(field, Hours::from_field);
306 let days_of_month = map_res(field_with_any, DaysOfMonth::from_field);
307 let months = map_res(field, Months::from_field);
308 let days_of_week = map_res(field_with_any, DaysOfWeek::from_field);
309 let years = opt(map_res(field, Years::from_field));
310 let fields = tuple((seconds, minutes, hours, days_of_month, months, days_of_week, years));
311
312 map(
313 terminated(fields, eof),
314 |(seconds, minutes, hours, days_of_month, months, days_of_week, years)| {
315 let years = years.unwrap_or_else(Years::all);
316 ScheduleFields::new(
317 seconds,
318 minutes,
319 hours,
320 days_of_month,
321 months,
322 days_of_week,
323 years,
324 )
325 },
326 )(i)
327}
328
329fn schedule(i: &str) -> IResult<&str, ScheduleFields> {
330 all_consuming(alt((shorthand, longhand)))(i)
331}
332
333#[cfg(test)]
334mod test {
335 use super::*;
336
337 #[test]
338 fn test_nom_valid_number() {
339 let expression = "1997";
340 point(expression).unwrap();
341 }
342
343 #[test]
344 fn test_nom_invalid_point() {
345 let expression = "a";
346 assert!(point(expression).is_err());
347 }
348
349 #[test]
350 fn test_nom_valid_named_point() {
351 let expression = "WED";
352 named_point(expression).unwrap();
353 }
354
355 #[test]
356 fn test_nom_invalid_named_point() {
357 let expression = "8";
358 assert!(named_point(expression).is_err());
359 }
360
361 #[test]
362 fn test_nom_valid_period() {
363 let expression = "1/2";
364 period(expression).unwrap();
365 }
366
367 #[test]
368 fn test_nom_invalid_period() {
369 let expression = "Wed/4";
370 assert!(period(expression).is_err());
371 }
372
373 #[test]
374 fn test_nom_valid_number_list() {
375 let expression = "1,2";
376 field(expression).unwrap();
377 field_with_any(expression).unwrap();
378 }
379
380 #[test]
381 fn test_nom_invalid_number_list() {
382 let expression = ",1,2";
383 assert!(field(expression).is_err());
384 assert!(field_with_any(expression).is_err());
385 }
386
387 #[test]
388 fn test_nom_field_with_any_valid_any() {
389 let expression = "?";
390 field_with_any(expression).unwrap();
391 }
392
393 #[test]
394 fn test_nom_field_invalid_any() {
395 let expression = "?";
396 assert!(field(expression).is_err());
397 }
398
399 #[test]
400 fn test_nom_valid_range_field() {
401 let expression = "1-4";
402 range(expression).unwrap();
403 }
404
405 #[test]
406 fn test_nom_valid_last_week_day() {
407 let expression = "2W";
408 month_last_word_day(expression).unwrap();
409 }
410
411 #[test]
412 fn test_nom_valid_period_all() {
413 let expression = "*/2";
414 period(expression).unwrap();
415 }
416
417 #[test]
418 fn test_nom_valid_period_range() {
419 let expression = "10-20/2";
420 period(expression).unwrap();
421 }
422
423 #[test]
424 fn test_nom_valid_period_named_range() {
425 let expression = "Mon-Thurs/2";
426 period(expression).unwrap();
427
428 let expression = "February-November/2";
429 period(expression).unwrap();
430 }
431
432 #[test]
433 fn test_nom_valid_period_point() {
434 let expression = "10/2";
435 period(expression).unwrap();
436 }
437
438 #[test]
439 fn test_nom_invalid_period_any() {
440 let expression = "?/2";
441 assert!(period(expression).is_err());
442 }
443
444 #[test]
445 fn test_nom_invalid_period_named_point() {
446 let expression = "Tues/2";
447 assert!(period(expression).is_err());
448
449 let expression = "February/2";
450 assert!(period(expression).is_err());
451 }
452
453 #[test]
454 fn test_nom_invalid_period_specifier_range() {
455 let expression = "10-12/*";
456 assert!(period(expression).is_err());
457 }
458
459 #[test]
460 fn test_nom_valid_period_with_any_all() {
461 let expression = "*/2";
462 period_with_any(expression).unwrap();
463 }
464
465 #[test]
466 fn test_nom_valid_period_with_any_range() {
467 let expression = "10-20/2";
468 period_with_any(expression).unwrap();
469 }
470
471 #[test]
472 fn test_nom_valid_period_with_any_named_range() {
473 let expression = "Mon-Thurs/2";
474 period_with_any(expression).unwrap();
475
476 let expression = "February-November/2";
477 period_with_any(expression).unwrap();
478 }
479
480 #[test]
481 fn test_nom_valid_period_with_any_point() {
482 let expression = "10/2";
483 period_with_any(expression).unwrap();
484 }
485
486 #[test]
487 fn test_nom_valid_period_with_any_any() {
488 let expression = "?/2";
489 period_with_any(expression).unwrap();
490 }
491
492 #[test]
493 fn test_nom_invalid_period_with_any_named_point() {
494 let expression = "Tues/2";
495 assert!(period_with_any(expression).is_err());
496
497 let expression = "February/2";
498 assert!(period_with_any(expression).is_err());
499 }
500
501 #[test]
502 fn test_nom_invalid_period_with_any_specifier_range() {
503 let expression = "10-12/*";
504 assert!(period_with_any(expression).is_err());
505 }
506
507 #[test]
508 fn test_nom_invalid_range_field() {
509 let expression = "-4";
510 assert!(range(expression).is_err());
511 }
512
513 #[test]
514 fn test_nom_valid_named_range_field() {
515 let expression = "TUES-THURS";
516 named_range(expression).unwrap();
517 }
518
519 #[test]
520 fn test_nom_invalid_named_range_field() {
521 let expression = "3-THURS";
522 assert!(named_range(expression).is_err());
523 }
524
525 #[test]
526 fn test_nom_valid_schedule() {
527 let expression = "* * * * * *";
528 schedule(expression).unwrap();
529 }
530
531 #[test]
532 fn test_nom_invalid_schedule() {
533 let expression = "* * * *";
534 assert!(schedule(expression).is_err());
535 }
536
537 #[test]
538 fn test_nom_valid_seconds_list() {
539 let expression = "0,20,40 * * * * *";
540 schedule(expression).unwrap();
541 }
542
543 #[test]
544 fn test_nom_valid_seconds_range() {
545 let expression = "0-40 * * * * *";
546 schedule(expression).unwrap();
547 }
548
549 #[test]
550 fn test_nom_valid_seconds_mix() {
551 let expression = "0-5,58 * * * * *";
552 schedule(expression).unwrap();
553 }
554
555 #[test]
556 fn test_nom_invalid_seconds_range() {
557 let expression = "0-65 * * * * *";
558 assert!(schedule(expression).is_err());
559 }
560
561 #[test]
562 fn test_nom_invalid_seconds_list() {
563 let expression = "103,12 * * * * *";
564 assert!(schedule(expression).is_err());
565 }
566
567 #[test]
568 fn test_nom_invalid_seconds_mix() {
569 let expression = "0-5,102 * * * * *";
570 assert!(schedule(expression).is_err());
571 }
572
573 #[test]
574 fn test_nom_valid_days_of_week_list() {
575 let expression = "* * * * * MON,WED,FRI";
576 schedule(expression).unwrap();
577 }
578
579 #[test]
580 fn test_nom_invalid_days_of_week_list() {
581 let expression = "* * * * * MON,TURTLE";
582 assert!(schedule(expression).is_err());
583 }
584
585 #[test]
586 fn test_nom_valid_days_of_week_range() {
587 let expression = "* * * * * MON-FRI";
588 schedule(expression).unwrap();
589 }
590
591 #[test]
592 fn test_nom_invalid_days_of_week_range() {
593 let expression = "* * * * * BEAR-OWL";
594 assert!(schedule(expression).is_err());
595 }
596
597 #[test]
598 fn test_nom_invalid_period_with_range_specifier() {
599 let expression = "10-12/10-12 * * * * ?";
600 assert!(schedule(expression).is_err());
601 }
602
603 #[test]
604 fn test_nom_valid_days_of_month_any() {
605 let expression = "* * * ? * *";
606 schedule(expression).unwrap();
607 }
608
609 #[test]
610 fn test_nom_valid_days_of_week_any() {
611 let expression = "* * * * * ?";
612 schedule(expression).unwrap();
613 }
614
615 #[test]
616 fn test_nom_valid_days_of_month_any_days_of_week_specific() {
617 let expression = "* * * ? * Mon,Thu";
618 schedule(expression).unwrap();
619 }
620
621 #[test]
622 fn test_nom_valid_days_of_week_any_days_of_month_specific() {
623 let expression = "* * * 1,2 * ?";
624 schedule(expression).unwrap();
625 }
626
627 #[test]
628 fn test_nom_valid_dom_and_dow_any() {
629 let expression = "* * * ? * ?";
630 schedule(expression).unwrap();
631 }
632
633 #[test]
634 fn test_nom_invalid_other_fields_any() {
635 let expression = "? * * * * *";
636 assert!(schedule(expression).is_err());
637
638 let expression = "* ? * * * *";
639 assert!(schedule(expression).is_err());
640
641 let expression = "* * ? * * *";
642 assert!(schedule(expression).is_err());
643
644 let expression = "* * * * ? *";
645 assert!(schedule(expression).is_err());
646 }
647
648 #[test]
649 fn test_nom_invalid_trailing_characters() {
650 let expression = "* * * * * *foo *";
651 assert!(schedule(expression).is_err());
652
653 let expression = "* * * * * * * foo";
654 assert!(schedule(expression).is_err());
655 }
656
657 #[test]
659 fn shorthand_must_match_whole_input() {
660 let expression = "@dailyBla";
661 assert!(schedule(expression).is_err());
662 let expression = " @dailyBla ";
663 assert!(schedule(expression).is_err());
664 }
665}