1use std::collections::HashSet;
2use std::fmt;
3use std::fmt::Formatter;
4
5use crate::error::TimeUnitError;
6use crate::error::TimeUnitErrorKind::{InvalidRange, InvalidRangeEnd, InvalidRangeStart,
7 InvalidStep, InvalidValue, OutOfBounds};
8
9#[derive(Debug)]
10pub enum TimeUnitKind {
11 Minute,
12 Hour,
13 MonthDay,
14 Month,
15 WeekDay
16}
17
18impl fmt::Display for TimeUnitKind {
19 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
20 use TimeUnitKind::*;
21
22 f.write_str(match self {
23 Minute => "minute",
24 Hour => "hour",
25 MonthDay => "month day",
26 Month => "month",
27 WeekDay => "week day"
28 })
29 }
30}
31
32#[derive(Debug)]
33pub enum TimeUnitValue {
34 Wildcard,
35 ValidValues(HashSet<u32>)
36}
37
38impl TimeUnitValue {
39 pub fn includes(&self, value: u32) -> bool {
40 use TimeUnitValue::*;
41
42 match self {
43 Wildcard => true,
44 ValidValues(values) => values.contains(&value)
45 }
46 }
47
48 pub fn is_wildcard(&self) -> bool {
49 if let TimeUnitValue::Wildcard = self {
50 true
51 } else {
52 false
53 }
54 }
55}
56
57pub trait TimeUnit {
58 fn kind() -> TimeUnitKind;
59
60 fn new(value: TimeUnitValue) -> Self where Self: Sized;
61
62 fn parse_value(input: &str) -> Result<u32, TimeUnitError> {
63 let value: u32 = input.parse().map_err(|_| InvalidValue.err(input))?;
64 if value < Self::min_value() || value > Self::max_value() {
65 return Err(OutOfBounds.err(input));
66 }
67 Ok(value)
68 }
69
70 fn parse(input: &str) -> Result<Self, TimeUnitError> where Self: Sized {
71 if input == "*" {
72 return Ok(Self::new(TimeUnitValue::Wildcard))
73 }
74
75 let values: HashSet<u32> = input.split(",")
76 .map(|item| match item {
77 _ if item.contains("-") => {
78 let split: Vec<&str> = item.split("-").collect();
79 if split.len() != 2 {
80 return Err(InvalidRange.err(item))
81 }
82
83 let start = Self::parse_value(split[0]).map_err(|err|
84 InvalidRangeStart(Box::new(err)).err(item))?;
85 let end = Self::parse_value(split[1]).map_err(|err|
86 InvalidRangeEnd(Box::new(err)).err(item))?;
87 Ok((start..=end).collect())
88 }
89 _ if item.starts_with("*/") => {
90 if item.len() < 3 {
91 return Err(InvalidStep.err(item));
92 }
93
94 let step = Self::parse_value(&item[2..])
95 .map_err(|_| InvalidStep.err(item))?;
96 Ok((Self::min_value()..=Self::max_value()).step_by(step as usize).collect())
97 }
98 _ => Ok(vec![Self::parse_value(item)?])
99 })
100 .collect::<Result<Vec<Vec<u32>>, TimeUnitError>>()?
101 .into_iter()
102 .flatten()
103 .collect();
104
105 Ok(Self::new(TimeUnitValue::ValidValues(values)))
106 }
107
108 fn min_value() -> u32;
109
110 fn max_value() -> u32;
111
112 fn value(&self) -> &TimeUnitValue;
113}
114
115macro_rules! define_time_unit {
116 ($name:ident, $min:expr, $max:expr) => {
117 #[derive(Debug)]
118 pub struct $name {
119 value: TimeUnitValue
120 }
121
122 impl TimeUnit for $name {
123 fn kind() -> TimeUnitKind {
124 TimeUnitKind::$name
125 }
126
127 fn new(value: TimeUnitValue) -> Self where Self: Sized {
128 $name {
129 value
130 }
131 }
132
133 fn min_value() -> u32 {
134 $min
135 }
136
137 fn max_value() -> u32 {
138 $max
139 }
140
141 fn value(&self) -> &TimeUnitValue {
142 &self.value
143 }
144 }
145 }
146}
147
148define_time_unit!(Minute, 0, 59);
149define_time_unit!(Hour, 0, 23);
150define_time_unit!(MonthDay, 1, 31);
151define_time_unit!(Month, 1, 12);
152define_time_unit!(WeekDay, 1, 7);