cron/time_unit/
mod.rs

1mod days_of_month;
2mod days_of_week;
3mod hours;
4mod minutes;
5mod months;
6mod seconds;
7mod years;
8
9pub use self::days_of_month::DaysOfMonth;
10pub use self::days_of_week::DaysOfWeek;
11pub use self::hours::Hours;
12pub use self::minutes::Minutes;
13pub use self::months::Months;
14pub use self::seconds::Seconds;
15pub use self::years::Years;
16
17use crate::error::*;
18use crate::ordinal::{Ordinal, OrdinalSet, Pattern, OrdinalList};
19use crate::specifier::{RootSpecifier, Specifier};
20use std::borrow::Cow;
21use std::collections::btree_set;
22use std::iter;
23use std::ops::RangeBounds;
24
25pub struct OrdinalIter<'a> {
26    set_iter: btree_set::Iter<'a, Ordinal>,
27}
28
29impl<'a> Iterator for OrdinalIter<'a> {
30    type Item = Ordinal;
31    fn next(&mut self) -> Option<Ordinal> {
32        self.set_iter.next().copied()
33    }
34}
35
36impl<'a> DoubleEndedIterator for OrdinalIter<'a> {
37    fn next_back(&mut self) -> Option<Self::Item> {
38        self.set_iter.next_back().copied()
39    }
40}
41
42pub struct OrdinalRangeIter<'a> {
43    range_iter: btree_set::Range<'a, Ordinal>,
44}
45
46impl<'a> Iterator for OrdinalRangeIter<'a> {
47    type Item = Ordinal;
48    fn next(&mut self) -> Option<Ordinal> {
49        self.range_iter.next().copied()
50    }
51}
52
53impl<'a> DoubleEndedIterator for OrdinalRangeIter<'a> {
54    fn next_back(&mut self) -> Option<Self::Item> {
55        self.range_iter.next_back().copied()
56    }
57}
58
59/// Methods exposing a schedule's configured ordinals for each individual unit of time.
60/// # Example
61/// ```
62/// use cron::{Schedule,TimeUnitSpec};
63/// use std::ops::Bound::{Included,Excluded};
64/// use std::str::FromStr;
65///
66/// let expression = "* * * * * * 2015-2044";
67/// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
68///
69/// // Membership
70/// assert_eq!(true, schedule.years().includes(2031));
71/// assert_eq!(false, schedule.years().includes(1969));
72///
73/// // Number of years specified
74/// assert_eq!(30, schedule.years().count());
75///
76/// // Iterator
77/// let mut years_iter = schedule.years().iter();
78/// assert_eq!(Some(2015), years_iter.next());
79/// assert_eq!(Some(2016), years_iter.next());
80/// // ...
81///
82/// // Range Iterator
83/// let mut five_year_plan = schedule.years().range((Included(2017), Excluded(2017 + 5)));
84/// assert_eq!(Some(2017), five_year_plan.next());
85/// assert_eq!(Some(2018), five_year_plan.next());
86/// assert_eq!(Some(2019), five_year_plan.next());
87/// assert_eq!(Some(2020), five_year_plan.next());
88/// assert_eq!(Some(2021), five_year_plan.next());
89/// assert_eq!(None, five_year_plan.next());
90/// ```
91pub trait TimeUnitSpec {
92    /// Returns true if the provided ordinal was included in the schedule spec for the unit of time
93    /// being described.
94    /// # Example
95    /// ```
96    /// use cron::{Schedule,TimeUnitSpec};
97    /// use std::str::FromStr;
98    ///
99    /// let expression = "* * * * * * 2015-2044";
100    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
101    ///
102    /// // Membership
103    /// assert_eq!(true, schedule.years().includes(2031));
104    /// assert_eq!(false, schedule.years().includes(2004));
105    /// ```
106    fn includes(&self, ordinal: Ordinal) -> bool;
107
108    /// Provides an iterator which will return each included ordinal for this schedule in order from
109    /// lowest to highest.
110    /// # Example
111    /// ```
112    /// use cron::{Schedule,TimeUnitSpec};
113    /// use std::str::FromStr;
114    ///
115    /// let expression = "* * * * 5-8 * *";
116    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
117    ///
118    /// // Iterator
119    /// let mut summer = schedule.months().iter();
120    /// assert_eq!(Some(5), summer.next());
121    /// assert_eq!(Some(6), summer.next());
122    /// assert_eq!(Some(7), summer.next());
123    /// assert_eq!(Some(8), summer.next());
124    /// assert_eq!(None, summer.next());
125    /// ```
126    fn iter(&self) -> OrdinalIter<'_>;
127
128    /// Provides an iterator which will return each included ordinal within the specified range.
129    /// # Example
130    /// ```
131    /// use cron::{Schedule,TimeUnitSpec};
132    /// use std::ops::Bound::{Included,Excluded};
133    /// use std::str::FromStr;
134    ///
135    /// let expression = "* * * 1,15 * * *";
136    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
137    ///
138    /// // Range Iterator
139    /// let mut mid_month_paydays = schedule.days_of_month().range((Included(10), Included(20)));
140    /// assert_eq!(Some(15), mid_month_paydays.next());
141    /// assert_eq!(None, mid_month_paydays.next());
142    /// ```
143    fn range<R>(&self, range: R) -> OrdinalRangeIter<'_>
144    where
145        R: RangeBounds<Ordinal>;
146
147    /// Returns the number of ordinals included in the associated schedule
148    /// # Example
149    /// ```
150    /// use cron::{Schedule,TimeUnitSpec};
151    /// use std::str::FromStr;
152    ///
153    /// let expression = "* * * 1,15 * * *";
154    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
155    ///
156    /// assert_eq!(2, schedule.days_of_month().count());
157    /// ```
158    fn count(&self) -> u32;
159
160    /// Checks if this TimeUnitSpec is defined as all possibilities (thus created with a '*', '?' or in the case of weekdays '1-7')
161    /// # Example
162    /// ```
163    /// use cron::{Schedule,TimeUnitSpec};
164    /// use std::str::FromStr;
165    ///
166    /// let expression = "* * * 1,15 * * *";
167    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
168    ///
169    /// assert_eq!(false, schedule.days_of_month().is_all());
170    /// assert_eq!(true, schedule.months().is_all());
171    /// ```
172    fn is_all(&self) -> bool;
173}
174
175impl<T> TimeUnitSpec for T
176where
177    T: TimeUnitField,
178{
179    fn includes(&self, ordinal: Ordinal) -> bool {
180        self.ordinals().contains(&ordinal)
181    }
182    fn iter(&self) -> OrdinalIter<'_> {
183        OrdinalIter {
184            set_iter: TimeUnitField::ordinals(self).iter(),
185        }
186    }
187    fn range<R>(&'_ self, range: R) -> OrdinalRangeIter<'_>
188    where
189        R: RangeBounds<Ordinal>,
190    {
191        OrdinalRangeIter {
192            range_iter: TimeUnitField::ordinals(self).range(range),
193        }
194    }
195    fn count(&self) -> u32 {
196        self.ordinals().len() as u32
197    }
198
199    fn is_all(&self) -> bool {
200        let max_supported_ordinals = Self::inclusive_max() - Self::inclusive_min() + 1;
201        self.ordinals().len() == max_supported_ordinals as usize
202    }
203}
204
205pub trait TimeUnitField
206where
207    Self: Sized,
208{
209    fn from_optional_ordinal_set(ordinal_set: Option<OrdinalSet>, pattern: Pattern, ordinal_list: OrdinalList) -> Self;
210    fn name() -> Cow<'static, str>;
211    fn inclusive_min() -> Ordinal;
212    fn inclusive_max() -> Ordinal;
213    fn ordinals(&self) -> &OrdinalSet;
214    fn ordinal_list(&self) -> &OrdinalList;
215    fn matching_pattern(&self) -> &str;
216    
217    fn from_ordinal(ordinal: Ordinal, matching_pattern: String, ordinal_list: OrdinalList) -> Self {
218        Self::from_ordinal_set(iter::once(ordinal).collect(), matching_pattern, ordinal_list)
219    }
220    
221    fn supported_ordinals() -> OrdinalSet {
222        (Self::inclusive_min()..Self::inclusive_max() + 1).collect()
223    }    
224    
225    fn all() -> Self {
226        Self::from_optional_ordinal_set(None, "value".to_string(), OrdinalList::new())
227    }
228    
229    fn from_ordinal_set(ordinal_set: OrdinalSet, matching_pattern: String, ordinal_list: OrdinalList) -> Self {
230        Self::from_optional_ordinal_set(Some(ordinal_set), matching_pattern, ordinal_list)
231    }
232    
233    fn ordinal_from_name(name: &str) -> Result<Ordinal, Error> {
234        Err(ErrorKind::Expression(format!(
235            "The '{}' field does not support using names. '{}' \
236             specified.",
237            Self::name(),
238            name
239        ))
240        .into())
241    }
242    
243    fn validate_ordinal(ordinal: Ordinal) -> Result<Ordinal, Error> {
244        // println!("validate_ordinal for {} => {}", Self::name(), ordinal);
245        match ordinal {
246            i if i < Self::inclusive_min() => Err(ErrorKind::Expression(format!(
247                "{} must be greater than or equal to {}. ('{}' \
248                 specified.)",
249                Self::name(),
250                Self::inclusive_min(),
251                i
252            ))
253            .into()),
254            i if i > Self::inclusive_max() => Err(ErrorKind::Expression(format!(
255                "{} must be less than {}. ('{}' specified.)",
256                Self::name(),
257                Self::inclusive_max(),
258                i
259            ))
260            .into()),
261            i => Ok(i),
262        }
263    }
264
265    fn ordinals_from_specifier(specifier: &Specifier) -> Result<(OrdinalSet, Pattern, OrdinalList), Error> {
266        use self::Specifier::*;
267        //println!("ordinals_from_specifier for {} => {:?}", Self::name(), specifier);
268        match specifier {
269            All => Ok((Self::supported_ordinals(), "value".to_string(), OrdinalList::new())),
270            Point(ordinal) => {
271                let mut os = OrdinalSet::new();
272                os.insert(*ordinal);
273                Ok((os, "value".to_string(), OrdinalList::new()))
274            },
275            Range(start, end) => {
276                match (Self::validate_ordinal(*start), Self::validate_ordinal(*end)) {
277                    (Ok(start), Ok(end)) if start <= end => Ok(((start..end + 1).collect(), "value".to_string(), OrdinalList::new())),
278                    _ => Err(ErrorKind::Expression(format!(
279                        "Invalid range for {}: {}-{}",
280                        Self::name(),
281                        start,
282                        end
283                    ))
284                    .into()),
285                }
286            }
287            NamedRange(ref start_name, ref end_name) => {
288                let start = Self::ordinal_from_name(start_name)?;
289                let end = Self::ordinal_from_name(end_name)?;
290                match (Self::validate_ordinal(start), Self::validate_ordinal(end)) {
291                    (Ok(start), Ok(end)) if start <= end => Ok(((start..end + 1).collect(), "value".to_string(), OrdinalList::new())),
292                    _ => Err(ErrorKind::Expression(format!(
293                        "Invalid named range for {}: {}-{}",
294                        Self::name(),
295                        start_name,
296                        end_name
297                    ))
298                    .into()),
299                }
300            }
301            MonthLast(pattern) => {
302                Ok((OrdinalSet::new(), pattern.to_string(), OrdinalList::new()))
303            }
304            MonthLastWithNum(num, pattern) => {
305                let mut os = OrdinalSet::new();
306                os.insert(*num);
307                Ok((os, pattern.to_string(), OrdinalList::new()))
308            },
309            MonthWeek(week, week_num, pattern) => {
310                let mut os = OrdinalList::new();
311                os.push(*week);
312                os.push(*week_num);
313                Ok((OrdinalSet::new(), pattern.to_string(), os))
314            },
315        }
316    }
317
318    fn ordinals_from_root_specifier(root_specifier: &RootSpecifier) -> Result<(OrdinalSet, Pattern, OrdinalList), Error> {
319        let (ordinals, pattern, ordinal_list) = match root_specifier {
320            RootSpecifier::Specifier(specifier) => Self::ordinals_from_specifier(specifier)?,
321            RootSpecifier::Period(_, 0) => Err(ErrorKind::Expression(format!("range step cannot be zero")))?,
322            RootSpecifier::Period(start, step) => {
323                let (base_set, pattern, ordinal_list) = match start {
324                    // A point prior to a period implies a range whose start is the specified
325                    // point and terminating inclusively with the inclusive max
326                    Specifier::Point(start) => {
327                        let start = Self::validate_ordinal(*start)?;
328                        ((start..=Self::inclusive_max()).collect(), "value".to_string(), OrdinalList::new())
329                    }
330                    specifier => Self::ordinals_from_specifier(specifier)?,
331                };
332                (base_set.into_iter().step_by(*step as usize).collect(), pattern, ordinal_list)
333            }
334            RootSpecifier::NamedPoint(ref name) => ((&[Self::ordinal_from_name(name)?])
335                .iter()
336                .cloned()
337                .collect::<OrdinalSet>(), "value".to_string(), OrdinalList::new()),
338        };
339        Ok((ordinals, pattern, ordinal_list))
340    }
341}