clockwork_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};
19use crate::specifier::{RootSpecifier, Specifier};
20use std::borrow::Cow;
21use std::iter;
22
23/// Methods exposing a schedule's configured ordinals for each individual unit of time.
24/// # Example
25/// ```
26/// use clockwork_cron::{Schedule,TimeUnitSpec};
27/// use std::ops::Bound::{Included,Excluded};
28/// use std::str::FromStr;
29///
30/// let expression = "* * * * * * 2015-2044";
31/// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
32///
33/// // Membership
34/// assert_eq!(true, schedule.years().includes(2031));
35/// assert_eq!(false, schedule.years().includes(1969));
36///
37/// // Number of years specified
38/// assert_eq!(30, schedule.years().count());
39/// ```
40pub trait TimeUnitSpec {
41    /// Returns true if the provided ordinal was included in the schedule spec for the unit of time
42    /// being described.
43    /// # Example
44    /// ```
45    /// use clockwork_cron::{Schedule,TimeUnitSpec};
46    /// use std::str::FromStr;
47    ///
48    /// let expression = "* * * * * * 2015-2044";
49    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
50    ///
51    /// // Membership
52    /// assert_eq!(true, schedule.years().includes(2031));
53    /// assert_eq!(false, schedule.years().includes(2004));
54    /// ```
55    fn includes(&self, ordinal: Ordinal) -> bool;
56
57    /// Returns the number of ordinals included in the associated schedule
58    /// # Example
59    /// ```
60    /// use clockwork_cron::{Schedule,TimeUnitSpec};
61    /// use std::str::FromStr;
62    ///
63    /// let expression = "* * * 1,15 * * *";
64    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
65    ///
66    /// assert_eq!(2, schedule.days_of_month().count());
67    /// ```
68    fn count(&self) -> u32;
69
70    /// Checks if this TimeUnitSpec is defined as all possibilities (thus created with a '*', '?' or in the case of weekdays '1-7')
71    /// # Example
72    /// ```
73    /// use clockwork_cron::{Schedule,TimeUnitSpec};
74    /// use std::str::FromStr;
75    ///
76    /// let expression = "* * * 1,15 * * *";
77    /// let schedule = Schedule::from_str(expression).expect("Failed to parse expression.");
78    ///
79    /// assert_eq!(false, schedule.days_of_month().is_all());
80    /// assert_eq!(true, schedule.months().is_all());
81    /// ```
82    fn is_all(&self) -> bool;
83}
84
85impl<T> TimeUnitSpec for T
86where
87    T: TimeUnitField,
88{
89    fn includes(&self, ordinal: Ordinal) -> bool {
90        self.ordinals().contains(&ordinal)
91    }
92
93    fn count(&self) -> u32 {
94        self.ordinals().len() as u32
95    }
96
97    fn is_all(&self) -> bool {
98        let max_supported_ordinals = Self::inclusive_max() - Self::inclusive_min() + 1;
99        self.ordinals().len() == max_supported_ordinals as usize
100    }
101}
102
103pub trait TimeUnitField
104where
105    Self: Sized,
106{
107    fn from_optional_ordinal_set(ordinal_set: Option<OrdinalSet>) -> Self;
108    fn name() -> Cow<'static, str>;
109    fn inclusive_min() -> Ordinal;
110    fn inclusive_max() -> Ordinal;
111    fn ordinals(&self) -> OrdinalSet;
112
113    fn from_ordinal(ordinal: Ordinal) -> Self {
114        Self::from_ordinal_set(iter::once(ordinal).collect())
115    }
116
117    fn supported_ordinals() -> OrdinalSet {
118        (Self::inclusive_min()..Self::inclusive_max() + 1).collect()
119    }
120
121    fn all() -> Self {
122        Self::from_optional_ordinal_set(None)
123    }
124
125    fn from_ordinal_set(ordinal_set: OrdinalSet) -> Self {
126        Self::from_optional_ordinal_set(Some(ordinal_set))
127    }
128
129    fn ordinal_from_name(name: &str) -> Result<Ordinal, Error> {
130        Err(ErrorKind::Expression(format!(
131            "The '{}' field does not support using names. '{}' \
132             specified.",
133            Self::name(),
134            name
135        ))
136        .into())
137    }
138
139    fn validate_ordinal(ordinal: Ordinal) -> Result<Ordinal, Error> {
140        //println!("validate_ordinal for {} => {}", Self::name(), ordinal);
141        match ordinal {
142            i if i < Self::inclusive_min() => Err(ErrorKind::Expression(format!(
143                "{} must be greater than or equal to {}. ('{}' \
144                 specified.)",
145                Self::name(),
146                Self::inclusive_min(),
147                i
148            ))
149            .into()),
150            i if i > Self::inclusive_max() => Err(ErrorKind::Expression(format!(
151                "{} must be less than {}. ('{}' specified.)",
152                Self::name(),
153                Self::inclusive_max(),
154                i
155            ))
156            .into()),
157            i => Ok(i),
158        }
159    }
160
161    fn ordinals_from_specifier(specifier: &Specifier) -> Result<OrdinalSet, Error> {
162        use self::Specifier::*;
163        //println!("ordinals_from_specifier for {} => {:?}", Self::name(), specifier);
164        match *specifier {
165            All => Ok(Self::supported_ordinals().clone()),
166            Point(ordinal) => Ok((&[ordinal]).iter().cloned().collect()),
167            Range(start, end) => {
168                match (Self::validate_ordinal(start), Self::validate_ordinal(end)) {
169                    (Ok(start), Ok(end)) if start <= end => Ok((start..end + 1).collect()),
170                    _ => Err(ErrorKind::Expression(format!(
171                        "Invalid range for {}: {}-{}",
172                        Self::name(),
173                        start,
174                        end
175                    ))
176                    .into()),
177                }
178            }
179            NamedRange(ref start_name, ref end_name) => {
180                let start = Self::ordinal_from_name(start_name)?;
181                let end = Self::ordinal_from_name(end_name)?;
182                match (Self::validate_ordinal(start), Self::validate_ordinal(end)) {
183                    (Ok(start), Ok(end)) if start <= end => Ok((start..end + 1).collect()),
184                    _ => Err(ErrorKind::Expression(format!(
185                        "Invalid named range for {}: {}-{}",
186                        Self::name(),
187                        start_name,
188                        end_name
189                    ))
190                    .into()),
191                }
192            }
193        }
194    }
195
196    fn ordinals_from_root_specifier(root_specifier: &RootSpecifier) -> Result<OrdinalSet, Error> {
197        let ordinals = match root_specifier {
198            RootSpecifier::Specifier(specifier) => Self::ordinals_from_specifier(specifier)?,
199            RootSpecifier::Period(start, step) => {
200                let base_set = match start {
201                    // A point prior to a period implies a range whose start is the specified
202                    // point and terminating inclusively with the inclusive max
203                    Specifier::Point(start) => {
204                        let start = Self::validate_ordinal(*start)?;
205                        (start..=Self::inclusive_max()).collect()
206                    }
207                    specifier => Self::ordinals_from_specifier(specifier)?,
208                };
209                if *step == 0 {
210                    return Err(ErrorKind::Expression(format!("step cannot be 0")).into());
211                }
212                base_set.into_iter().step_by(*step as usize).collect()
213            }
214            RootSpecifier::NamedPoint(ref name) => (&[Self::ordinal_from_name(name)?])
215                .iter()
216                .cloned()
217                .collect::<OrdinalSet>(),
218        };
219        Ok(ordinals)
220    }
221}