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}