mod days_of_month;
mod days_of_week;
mod hours;
mod minutes;
mod months;
mod seconds;
mod years;
pub use self::days_of_month::DaysOfMonth;
pub use self::days_of_week::DaysOfWeek;
pub use self::hours::Hours;
pub use self::minutes::Minutes;
pub use self::months::Months;
pub use self::seconds::Seconds;
pub use self::years::Years;
use crate::error::*;
use crate::ordinal::{Ordinal, OrdinalSet};
use crate::specifier::{RootSpecifier, Specifier};
use std::borrow::Cow;
use std::collections::btree_set;
use std::iter;
use std::ops::RangeBounds;
pub struct OrdinalIter<'a> {
set_iter: btree_set::Iter<'a, Ordinal>,
}
impl Iterator for OrdinalIter<'_> {
type Item = Ordinal;
fn next(&mut self) -> Option<Ordinal> {
self.set_iter.next().copied()
}
}
impl DoubleEndedIterator for OrdinalIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.set_iter.next_back().copied()
}
}
pub struct OrdinalRangeIter<'a> {
range_iter: btree_set::Range<'a, Ordinal>,
}
impl Iterator for OrdinalRangeIter<'_> {
type Item = Ordinal;
fn next(&mut self) -> Option<Ordinal> {
self.range_iter.next().copied()
}
}
impl DoubleEndedIterator for OrdinalRangeIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.range_iter.next_back().copied()
}
}
pub trait TimeUnitSpec {
fn includes(&self, ordinal: Ordinal) -> bool;
fn iter(&self) -> OrdinalIter<'_>;
fn range<R>(&self, range: R) -> OrdinalRangeIter<'_>
where
R: RangeBounds<Ordinal>;
fn count(&self) -> u32;
fn is_all(&self) -> bool;
}
impl<T> TimeUnitSpec for T
where
T: TimeUnitField,
{
fn includes(&self, ordinal: Ordinal) -> bool {
self.ordinals().contains(&ordinal)
}
fn iter(&self) -> OrdinalIter<'_> {
OrdinalIter {
set_iter: TimeUnitField::ordinals(self).iter(),
}
}
fn range<R>(&'_ self, range: R) -> OrdinalRangeIter<'_>
where
R: RangeBounds<Ordinal>,
{
OrdinalRangeIter {
range_iter: TimeUnitField::ordinals(self).range(range),
}
}
fn count(&self) -> u32 {
self.ordinals().len() as u32
}
fn is_all(&self) -> bool {
let max_supported_ordinals = Self::inclusive_max() - Self::inclusive_min() + 1;
self.ordinals().len() == max_supported_ordinals as usize
}
}
pub trait TimeUnitField
where
Self: Sized,
{
fn from_optional_ordinal_set(ordinal_set: Option<OrdinalSet>) -> Self;
fn name() -> Cow<'static, str>;
fn inclusive_min() -> Ordinal;
fn inclusive_max() -> Ordinal;
fn ordinals(&self) -> &OrdinalSet;
fn from_ordinal(ordinal: Ordinal) -> Self {
Self::from_ordinal_set(iter::once(ordinal).collect())
}
fn supported_ordinals() -> OrdinalSet {
(Self::inclusive_min()..Self::inclusive_max() + 1).collect()
}
fn all() -> Self {
Self::from_optional_ordinal_set(None)
}
fn from_ordinal_set(ordinal_set: OrdinalSet) -> Self {
Self::from_optional_ordinal_set(Some(ordinal_set))
}
fn ordinal_from_name(name: &str) -> Result<Ordinal, Error> {
Err(ErrorKind::Expression(format!(
"The '{}' field does not support using names. '{}' \
specified.",
Self::name(),
name
))
.into())
}
fn validate_ordinal(ordinal: Ordinal) -> Result<Ordinal, Error> {
match ordinal {
i if i < Self::inclusive_min() => Err(ErrorKind::Expression(format!(
"{} must be greater than or equal to {}. ('{}' \
specified.)",
Self::name(),
Self::inclusive_min(),
i
))
.into()),
i if i > Self::inclusive_max() => Err(ErrorKind::Expression(format!(
"{} must be less than {}. ('{}' specified.)",
Self::name(),
Self::inclusive_max(),
i
))
.into()),
i => Ok(i),
}
}
fn ordinals_from_specifier(specifier: &Specifier) -> Result<OrdinalSet, Error> {
use self::Specifier::*;
match *specifier {
All => Ok(Self::supported_ordinals()),
Point(ordinal) => Ok(([ordinal]).iter().cloned().collect()),
Range(start, end) => {
match (Self::validate_ordinal(start), Self::validate_ordinal(end)) {
(Ok(start), Ok(end)) if start <= end => Ok((start..end + 1).collect()),
_ => Err(ErrorKind::Expression(format!(
"Invalid range for {}: {}-{}",
Self::name(),
start,
end
))
.into()),
}
}
NamedRange(ref start_name, ref end_name) => {
let start = Self::ordinal_from_name(start_name)?;
let end = Self::ordinal_from_name(end_name)?;
match (Self::validate_ordinal(start), Self::validate_ordinal(end)) {
(Ok(start), Ok(end)) if start <= end => Ok((start..end + 1).collect()),
_ => Err(ErrorKind::Expression(format!(
"Invalid named range for {}: {}-{}",
Self::name(),
start_name,
end_name
))
.into()),
}
}
}
}
fn ordinals_from_root_specifier(root_specifier: &RootSpecifier) -> Result<OrdinalSet, Error> {
let ordinals = match root_specifier {
RootSpecifier::Specifier(specifier) => Self::ordinals_from_specifier(specifier)?,
RootSpecifier::Period(_, 0) => Err(ErrorKind::Expression(
"range step cannot be zero".to_string(),
))?,
RootSpecifier::Period(start, step) => {
if *step < 1 || *step > Self::inclusive_max() {
return Err(ErrorKind::Expression(format!(
"{} must be between 1 and {}. ('{}' specified.)",
Self::name(),
Self::inclusive_max(),
step,
))
.into());
}
let base_set = match start {
Specifier::Point(start) => {
let start = Self::validate_ordinal(*start)?;
(start..=Self::inclusive_max()).collect()
}
specifier => Self::ordinals_from_specifier(specifier)?,
};
base_set.into_iter().step_by(*step as usize).collect()
}
RootSpecifier::NamedPoint(ref name) => ([Self::ordinal_from_name(name)?])
.iter()
.cloned()
.collect::<OrdinalSet>(),
};
Ok(ordinals)
}
}