use crate::RRuleResult;
use crate::{iter::rrule_iter::WasLimited, Tz};
use std::ops::{
Bound::{Excluded, Unbounded},
RangeBounds,
};
pub(super) fn collect_with_error<T>(
mut iterator: T,
start: &Option<chrono::DateTime<Tz>>,
end: &Option<chrono::DateTime<Tz>>,
inclusive: bool,
limit: Option<u16>,
) -> RRuleResult
where
T: Iterator<Item = chrono::DateTime<Tz>> + WasLimited,
{
let mut list = vec![];
let mut was_limited = false;
while limit.is_none() || matches!(limit, Some(limit) if usize::from(limit) > list.len()) {
if let Some(value) = iterator.next() {
if is_in_range(&value, start, end, inclusive) {
list.push(value);
}
if has_reached_the_end(&value, end, inclusive) {
break;
}
} else {
was_limited = iterator.was_limited();
break;
}
}
was_limited = was_limited || matches!(limit, Some(limit) if usize::from(limit) == list.len());
RRuleResult {
dates: list,
limited: was_limited,
}
}
fn has_reached_the_end(
date: &chrono::DateTime<Tz>,
end: &Option<chrono::DateTime<Tz>>,
inclusive: bool,
) -> bool {
if inclusive {
match end {
Some(end) => !(..=end).contains(&date),
None => false,
}
} else {
match end {
Some(end) => !(Unbounded, Excluded(end)).contains(date),
None => false,
}
}
}
pub(super) fn is_in_range(
date: &chrono::DateTime<Tz>,
start: &Option<chrono::DateTime<Tz>>,
end: &Option<chrono::DateTime<Tz>>,
inclusive: bool,
) -> bool {
if inclusive {
match (start, end) {
(Some(start), Some(end)) => (start..=end).contains(&date),
(Some(start), None) => (start..).contains(&date),
(None, Some(end)) => (..=end).contains(&date),
(None, None) => true,
}
} else {
match (start, end) {
(Some(start), Some(end)) => (Excluded(start), Excluded(end)).contains(date),
(Some(start), None) => (Excluded(start), Unbounded).contains(date),
(None, Some(end)) => (Unbounded, Excluded(end)).contains(date),
(None, None) => true,
}
}
}
#[cfg(test)]
mod tests {
use crate::core::Tz;
use super::*;
use chrono::TimeZone;
const UTC: Tz = Tz::UTC;
#[test]
fn in_range_exclusive_start_to_end() {
let inclusive = false;
let start = UTC.with_ymd_and_hms(2021, 10, 1, 8, 0, 0).unwrap();
let end = UTC.with_ymd_and_hms(2021, 10, 1, 10, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 7, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 11, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(!is_in_range(&end, &Some(start), &Some(end), inclusive));
assert!(!is_in_range(&start, &Some(start), &Some(end), inclusive));
}
#[test]
fn in_range_exclusive_start() {
let inclusive = false;
let start = UTC.with_ymd_and_hms(2021, 10, 1, 8, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 7, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(!is_in_range(&start, &Some(start), &None, inclusive));
}
#[test]
fn in_range_exclusive_end() {
let inclusive = false;
let end = UTC.with_ymd_and_hms(2021, 10, 1, 10, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 9, 20, 10, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(!is_in_range(&end, &None, &Some(end), inclusive));
}
#[test]
fn in_range_exclusive_all() {
let inclusive = false;
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 9, 20, 10, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
}
#[test]
fn in_range_inclusive_start_to_end() {
let inclusive = true;
let start = UTC.with_ymd_and_hms(2021, 10, 1, 8, 0, 0).unwrap();
let end = UTC.with_ymd_and_hms(2021, 10, 1, 10, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 7, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 11, 0, 0).unwrap(),
&Some(start),
&Some(end),
inclusive,
));
assert!(is_in_range(&end, &Some(start), &Some(end), inclusive));
assert!(is_in_range(&start, &Some(start), &Some(end), inclusive));
}
#[test]
fn in_range_inclusive_start() {
let inclusive = true;
let start = UTC.with_ymd_and_hms(2021, 10, 1, 8, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 7, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&Some(start),
&None,
inclusive,
));
assert!(is_in_range(&start, &Some(start), &None, inclusive));
}
#[test]
fn in_range_inclusive_end() {
let inclusive = true;
let end = UTC.with_ymd_and_hms(2021, 10, 1, 10, 0, 0).unwrap();
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 9, 20, 10, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(!is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&None,
&Some(end),
inclusive,
));
assert!(is_in_range(&end, &None, &Some(end), inclusive));
}
#[test]
fn in_range_inclusive_all() {
let inclusive = true;
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 1, 9, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 9, 20, 10, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
assert!(is_in_range(
&UTC.with_ymd_and_hms(2021, 10, 2, 8, 0, 0).unwrap(),
&None,
&None,
inclusive,
));
}
}