///|
/// Lazy naive recurrence snapshot with checked reads. The first next() may
/// report an unreachable clock cycle; a failed or exhausted reader then ends.
pub struct RRuleDateIterator {
priv core : RRule
priv source : Iter[DateTime]
priv mut checked : Bool
priv mut finished : Bool
}
///|
fn RRule::check_clock_iteration(self : RRule) -> Unit raise DateError {
let unit = match self.rule.frequency {
Minutely => 60
Secondly => 1
_ => return
}
if recurrence_clock_slots(self.rule, self.dtstart(), unit).next() is None {
raise DateError::InvalidDate(
message="recurrence has no reachable clock cycle",
)
}
}
///|
fn RRuleWrapper::checked_reader(
self : RRuleWrapper,
lower? : DateTime,
upper? : DateTime,
) -> RRuleDateIterator {
{
core: self.core,
source: self.naive_reader(lower?, upper?),
checked: false,
finished: false,
}
}
///|
fn RRuleDateIterator::next_date(
self : RRuleDateIterator,
) -> DateTime? raise DateError {
if self.finished {
return None
}
if !self.checked {
self.checked = true
errdefer {
self.finished = true
}
self.core.check_clock_iteration()
}
match self.source.next() {
Some(value) => Some(value)
None => {
self.finished = true
None
}
}
}
///|
/// Read the next naive date. Creating the reader performs no clock/epoch access
/// and does not raise an iteration error. A read error terminates this reader.
pub fn RRuleDateIterator::next(
self : RRuleDateIterator,
) -> RRuleDateTime? raise DateError {
self.next_date().map(RRuleDateTime::naive)
}
///|
/// Snapshot the successful core and its existing cache, deferring validation to
/// next(). Later wrapper.set calls do not change this reader's recurrence.
///
/// ```mbt check
/// test {
/// let rule = @date.RRuleWrapper::new(
/// @date.Minutely,
/// dtstart=@date.RRuleDateTime::naive(@date.datetime(2024, 1, 1, hour=12)),
/// interval=1440,
/// byhour=[12],
/// byminute=[0],
/// count=2,
/// cache=true,
/// )
/// let reader = rule.checked_raw_iter()
/// assert_eq(reader.next().unwrap().date().day(), 1)
/// rule.set(byhour=[13])
/// assert_eq(reader.next().unwrap().date().day(), 2)
/// assert_true(
/// try {
/// ignore(rule.occurrence_count())
/// false
/// } catch {
/// _ => true
/// },
/// )
/// }
/// ```
pub fn RRuleWrapper::checked_raw_iter(self : RRuleWrapper) -> RRuleDateIterator {
self.checked_reader()
}