feat(sched): ✨ wrapping ranges
This commit is contained in:
parent
bb727ec62c
commit
325e98c774
@ -32,18 +32,25 @@ where
|
|||||||
match matcher {
|
match matcher {
|
||||||
Rule::Val(v) => value == *v,
|
Rule::Val(v) => value == *v,
|
||||||
Rule::Range(start, end, step) => {
|
Rule::Range(start, end, step) => {
|
||||||
if *step == T::one() {
|
if *start == *end {
|
||||||
value >= *start && value <= *end
|
return value == *start;
|
||||||
} else {
|
}
|
||||||
let mut current = *start;
|
|
||||||
while current <= *end {
|
if *step == T::zero() || *step == T::one() {
|
||||||
if current == value {
|
if *start < *end {
|
||||||
return true;
|
return value >= *start && value <= *end;
|
||||||
}
|
} else {
|
||||||
// Move to the next value based on the step size
|
return value >= *start || value <= *end;
|
||||||
current = current + *step;
|
}
|
||||||
|
} else {
|
||||||
|
if *start < *end {
|
||||||
|
return value >= *start
|
||||||
|
&& value <= *end
|
||||||
|
&& (value - *start) % *step == T::zero();
|
||||||
|
} else {
|
||||||
|
return (value >= *start || value <= *end)
|
||||||
|
&& (*start - value) % *step == T::zero();
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rule::Many(matcher) => matcher.iter().any(|v| Self::_matches(&Rule::Val(*v), value)),
|
Rule::Many(matcher) => matcher.iter().any(|v| Self::_matches(&Rule::Val(*v), value)),
|
||||||
@ -84,3 +91,85 @@ pub fn many<T: PrimInt>(values: Vec<T>) -> Rule<T> {
|
|||||||
pub fn ranges<T: PrimInt>(ranges: Vec<(T, T, T)>) -> Rule<T> {
|
pub fn ranges<T: PrimInt>(ranges: Vec<(T, T, T)>) -> Rule<T> {
|
||||||
Rule::Ranges(ranges)
|
Rule::Ranges(ranges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_val() {
|
||||||
|
let rule = val(5);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(!rule.matches(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_range() {
|
||||||
|
let rule = range(5, 10, 1);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(rule.matches(6));
|
||||||
|
assert!(rule.matches(10));
|
||||||
|
assert!(!rule.matches(11));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_range_step() {
|
||||||
|
let rule = range(5, 10, 2);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(!rule.matches(6));
|
||||||
|
assert!(rule.matches(7));
|
||||||
|
assert!(!rule.matches(8));
|
||||||
|
assert!(rule.matches(9));
|
||||||
|
assert!(!rule.matches(10));
|
||||||
|
assert!(!rule.matches(11));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_wrapping_range() {
|
||||||
|
// imagine a week where 0 is Sunday and 6 is Saturday
|
||||||
|
// s | m | t | w | t | f | s
|
||||||
|
// 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||||
|
|
||||||
|
// if a range rules goes from 5 to 2, it should match 5, 6, 0, 1, 2
|
||||||
|
// since our rule doesn't have max and min values, it should match any value
|
||||||
|
// >= 5 and any value <= 2, (well, taking step into account, obviously)
|
||||||
|
|
||||||
|
// TODO: maybe in the future we could add another rule type "WrappingRange" that would
|
||||||
|
// handle this case more explicitly and taking into account the min-max wrapping
|
||||||
|
// limits (like 0 and 6 in this case). It obviously would need its WrappingRanges
|
||||||
|
// analogs just like we have Ranges for Range... for now we can just use the
|
||||||
|
// Range rule and be happy with it
|
||||||
|
|
||||||
|
let rule = range(5, 2, 1);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(rule.matches(6));
|
||||||
|
assert!(rule.matches(0));
|
||||||
|
assert!(rule.matches(1));
|
||||||
|
assert!(rule.matches(2));
|
||||||
|
assert!(!rule.matches(3));
|
||||||
|
assert!(!rule.matches(4));
|
||||||
|
|
||||||
|
// would match any value >= 5 too
|
||||||
|
assert!(rule.matches(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_many() {
|
||||||
|
let rule = many(vec![5, 10, 15]);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(rule.matches(10));
|
||||||
|
assert!(rule.matches(15));
|
||||||
|
assert!(!rule.matches(11));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rule_ranges() {
|
||||||
|
let rule = ranges(vec![(5, 10, 1), (15, 20, 1)]);
|
||||||
|
assert!(rule.matches(5));
|
||||||
|
assert!(rule.matches(6));
|
||||||
|
assert!(rule.matches(10));
|
||||||
|
assert!(rule.matches(15));
|
||||||
|
assert!(rule.matches(20));
|
||||||
|
assert!(!rule.matches(11));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -92,11 +92,38 @@ impl RecurrenceRuleSet {
|
|||||||
self.time_rule(Rule::Val(hour), Rule::Val(minute), Rule::Val(second))
|
self.time_rule(Rule::Val(hour), Rule::Val(minute), Rule::Val(second))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 🧉 » set the day of the week rule as a single value from primitive
|
/// 🧉 » set the day of the week rule as a single value
|
||||||
pub fn on_dow(&mut self, value: Weekday) -> &mut Self {
|
pub fn on_weekday(&mut self, value: Weekday) -> &mut Self {
|
||||||
self.dow_rule(Rule::Val(value.num_days_from_sunday()))
|
self.dow_rule(Rule::Val(value.num_days_from_sunday()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🧉 » set the day of the week rule as a single value (`dow` from Sunday 0)
|
||||||
|
pub fn on_dow(&mut self, value: u32) -> &mut Self {
|
||||||
|
self.dow_rule(Rule::Val(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » set the day of the week rule as a range between two `Weekday`
|
||||||
|
pub fn from_to_weekdays(&mut self, from: Weekday, to: Weekday) -> &mut Self {
|
||||||
|
if from == to {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dow_rule(Rule::Range(
|
||||||
|
from.num_days_from_sunday(),
|
||||||
|
to.num_days_from_sunday(),
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🧉 » set the day of the week rule as a range between two values (`dow` from Sunday 0)
|
||||||
|
pub fn from_to_dow(&mut self, from: u32, to: u32) -> &mut Self {
|
||||||
|
if from == to {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dow_rule(Rule::Range(from, to, 1))
|
||||||
|
}
|
||||||
|
|
||||||
/// 🧉 » set the day of the month rule as a single value from primitive
|
/// 🧉 » set the day of the month rule as a single value from primitive
|
||||||
pub fn on_day(&mut self, value: u32) -> &mut Self {
|
pub fn on_day(&mut self, value: u32) -> &mut Self {
|
||||||
self.day_ryle(Rule::Val(value))
|
self.day_ryle(Rule::Val(value))
|
||||||
|
|||||||
@ -97,7 +97,7 @@ fn each_wednesday() {
|
|||||||
let mut next_wednesday = Local.with_ymd_and_hms(2024, 4, 10, 0, 0, 0).unwrap();
|
let mut next_wednesday = Local.with_ymd_and_hms(2024, 4, 10, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
let mut rules = ruleset();
|
let mut rules = ruleset();
|
||||||
rules.on_dow(Weekday::Wed).at_time(0, 0, 0);
|
rules.on_weekday(Weekday::Wed).at_time(0, 0, 0);
|
||||||
|
|
||||||
let mut next = date;
|
let mut next = date;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user