use std::collections::HashSet;
use std::fmt;
use std::fmt::Formatter;
use crate::error::TimeUnitError;
use crate::error::TimeUnitErrorKind::{InvalidRange, InvalidRangeEnd, InvalidRangeStart,
InvalidStep, InvalidValue, OutOfBounds};
#[derive(Debug)]
pub enum TimeUnitKind {
Minute,
Hour,
MonthDay,
Month,
WeekDay
}
impl fmt::Display for TimeUnitKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use TimeUnitKind::*;
f.write_str(match self {
Minute => "minute",
Hour => "hour",
MonthDay => "month day",
Month => "month",
WeekDay => "week day"
})
}
}
#[derive(Debug)]
pub enum TimeUnitValue {
Wildcard,
ValidValues(HashSet<u32>)
}
impl TimeUnitValue {
pub fn includes(&self, value: u32) -> bool {
use TimeUnitValue::*;
match self {
Wildcard => true,
ValidValues(values) => values.contains(&value)
}
}
pub fn is_wildcard(&self) -> bool {
if let TimeUnitValue::Wildcard = self {
true
} else {
false
}
}
}
pub trait TimeUnit {
fn kind() -> TimeUnitKind;
fn new(value: TimeUnitValue) -> Self where Self: Sized;
fn parse_value(input: &str) -> Result<u32, TimeUnitError> {
let value: u32 = input.parse().map_err(|_| InvalidValue.err(input))?;
if value < Self::min_value() || value > Self::max_value() {
return Err(OutOfBounds.err(input));
}
Ok(value)
}
fn parse(input: &str) -> Result<Self, TimeUnitError> where Self: Sized {
if input == "*" {
return Ok(Self::new(TimeUnitValue::Wildcard))
}
let values: HashSet<u32> = input.split(",")
.map(|item| match item {
_ if item.contains("-") => {
let split: Vec<&str> = item.split("-").collect();
if split.len() != 2 {
return Err(InvalidRange.err(item))
}
let start = Self::parse_value(split[0]).map_err(|err|
InvalidRangeStart(Box::new(err)).err(item))?;
let end = Self::parse_value(split[1]).map_err(|err|
InvalidRangeEnd(Box::new(err)).err(item))?;
Ok((start..=end).collect())
}
_ if item.starts_with("*/") => {
if item.len() < 3 {
return Err(InvalidStep.err(item));
}
let step = Self::parse_value(&item[2..])
.map_err(|_| InvalidStep.err(item))?;
Ok((Self::min_value()..=Self::max_value()).step_by(step as usize).collect())
}
_ => Ok(vec![Self::parse_value(item)?])
})
.collect::<Result<Vec<Vec<u32>>, TimeUnitError>>()?
.into_iter()
.flatten()
.collect();
Ok(Self::new(TimeUnitValue::ValidValues(values)))
}
fn min_value() -> u32;
fn max_value() -> u32;
fn value(&self) -> &TimeUnitValue;
}
macro_rules! define_time_unit {
($name:ident, $min:expr, $max:expr) => {
#[derive(Debug)]
pub struct $name {
value: TimeUnitValue
}
impl TimeUnit for $name {
fn kind() -> TimeUnitKind {
TimeUnitKind::$name
}
fn new(value: TimeUnitValue) -> Self where Self: Sized {
$name {
value
}
}
fn min_value() -> u32 {
$min
}
fn max_value() -> u32 {
$max
}
fn value(&self) -> &TimeUnitValue {
&self.value
}
}
}
}
define_time_unit!(Minute, 0, 59);
define_time_unit!(Hour, 0, 23);
define_time_unit!(MonthDay, 1, 31);
define_time_unit!(Month, 1, 12);
define_time_unit!(WeekDay, 1, 7);