Skip to main content

cron/time_unit/
mod.rs

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