pub mod builder; use { super::Rule, crate::sched::utils::cron_date::LoolDate, chrono::{DateTime, Datelike, Local}, }; /// 🧉 » a recurrence rule-set /// /// sets rules that define a certain recurrence behavior /// /// use the builder pattern to create a new `RecurrenceRuleSet` pub struct RecurrenceRuleSet { /// second of the minute (0..59) second: Option>, /// minute of the hour (0..59) minute: Option>, /// hour of the day (0..23) hour: Option>, /// day of the week starting from sunday (`0=Sunday`, `1=Monday`, ..., `6=Saturday`) dow: Option>, /// day of the month (1..31) day: Option>, /// month of the year (1..12) month: Option>, /// year year: Option>, } impl RecurrenceRuleSet { /// 🧉 » returns the next match of the rule set from `now` pub fn next_match(&self) -> Option> { self.next_match_from(Local::now()) } /// 🧉 » returns the next match of the rule set from a given `DateTime` pub fn next_match_from(&self, from: DateTime) -> Option> { let next = self._next_match(from); next.map(|date| date.date()) } /// 🚧 internal fn _next_match(&self, from: DateTime) -> Option> { if !self.is_valid() { return None; } // check year if let Some(Rule::Val(year)) = self.year { if year < from.year() { return None; } } let mut next = LoolDate::new(from); next.add_second(); loop { // check other possible year values if let Some(year_unit) = &self.year { if let Rule::Val(year) = year_unit { if *year < next.year() { return None; } } if !year_unit.matches(next.year()) { next.add_year(); next.set_md(1, 1); next.set_hms(0, 0, 0); continue; } } if let Some(month_unit) = &self.month { if !month_unit.matches(next.month()) { next.add_month(); continue; } } if let Some(day_unit) = &self.day { if !day_unit.matches(next.day()) { next.add_day(); continue; } } if let Some(dow_unit) = &self.dow { if !dow_unit.matches(next.weekday_from_sunday()) { next.add_day(); continue; } } if let Some(hour_unit) = &self.hour { if !hour_unit.matches(next.hour()) { next.add_hour(); continue; } } if let Some(minute_unit) = &self.minute { if !minute_unit.matches(next.minute()) { next.add_minute(); continue; } } if let Some(second_unit) = &self.second { if !second_unit.matches(next.second()) { next.add_second(); continue; } } // finally, everything matches, so we get out of the loop break; } Some(next) } }