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