cron/
parsing.rs

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            } // Extract from nom tuple
25            Err(e) => {
26                println!("异常信息:{:?}", e);
27                Err(ErrorKind::Expression("Invalid cron expression.".to_owned()).into())
28            }, //TODO: Details
29        }
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>, // TODO: expose iterator?
43}
44
45trait FromField
46where
47    Self: Sized,
48{
49    //TODO: Replace with std::convert::TryFrom when stable
50    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    /// Issue #86
658    #[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}