1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::{Cron, CronError, Direction};
use chrono::{DateTime, TimeZone, Duration};
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct CronIterator<Tz>
where
Tz: TimeZone,
{
cron: Cron,
current_time: DateTime<Tz>,
is_first: bool,
inclusive: bool,
direction: Direction,
pending_ambiguous_dt: Option<DateTime<Tz>>,
}
impl<Tz> CronIterator<Tz>
where
Tz: TimeZone,
{
/// Creates a new `CronIterator`.
///
/// # Arguments
///
/// * `cron` - The `Cron` schedule instance.
/// * `start_time` - The `DateTime` to start iterating from.
/// * `inclusive` - Whether the `start_time` should be included in the results if it matches.
/// * `direction` - The direction to iterate in (Forward or Backward).
pub fn new(
cron: Cron,
start_time: DateTime<Tz>,
inclusive: bool,
direction: Direction,
) -> Self {
CronIterator {
cron,
current_time: start_time,
is_first: true,
inclusive,
direction,
pending_ambiguous_dt: None,
}
}
}
impl<Tz> Iterator for CronIterator<Tz>
where
Tz: TimeZone + Clone + Copy,
{
type Item = DateTime<Tz>;
fn next(&mut self) -> Option<Self::Item> {
// Step 1: Check for and yield a pending ambiguous datetime first.
// This handles the second occurrence of a time during DST fallback.
if let Some(pending_dt_to_yield) = self.pending_ambiguous_dt.take() {
// After yielding the second ambiguous time, advance current_time past it.
// Clone pending_dt_to_yield because it's about to be returned,
// but we need its value to calculate the next `self.current_time`.
self.current_time = pending_dt_to_yield.clone().checked_add_signed(match self.direction { // Fixed E0382: pending_dt_to_yield
Direction::Forward => Duration::seconds(1),
Direction::Backward => Duration::seconds(-1),
}).ok_or(CronError::InvalidTime).ok()?;
return Some(pending_dt_to_yield);
}
// Determine if the search should be inclusive based on whether it's the first run.
let inclusive_search = if self.is_first {
self.is_first = false;
self.inclusive
} else {
false // Subsequent searches are always exclusive of the last actual point in time.
};
let result = self.cron.find_occurrence(&self.current_time, inclusive_search, self.direction);
match result {
Ok((found_time, optional_second_ambiguous_dt)) => {
// This `found_time` is the one we will return in this iteration.
// If there's a second ambiguous datetime (for interval jobs),
// store it to be yielded on the *next* call to next().
// And importantly, set `self.current_time` to advance *past* this second ambiguous time
// so the *next* search for a *new* naive time is correct.
if let Some(second_ambiguous_dt) = optional_second_ambiguous_dt {
// Clone second_ambiguous_dt because it's stored in self.pending_ambiguous_dt
// AND used to calculate the next self.current_time.
self.pending_ambiguous_dt = Some(second_ambiguous_dt.clone()); // Fixed E0382: second_ambiguous_dt
// Advance `self.current_time` past the latest of the ambiguous pair.
// This ensures the next `find_occurrence` call searches for the next unique naive time.
self.current_time = second_ambiguous_dt.checked_add_signed(match self.direction {
Direction::Forward => Duration::seconds(1),
Direction::Backward => Duration::seconds(-1),
}).ok_or(CronError::InvalidTime).ok()?;
} else {
// Case: No second ambiguous time (either not an overlap, or fixed-time job).
// Advance `self.current_time` simply past the `found_time`.
// Clone found_time because it's used to calculate the next self.current_time
// AND returned at the end of this block.
self.current_time = found_time.clone().checked_add_signed(match self.direction { // Fixed E0382: found_time
Direction::Forward => Duration::seconds(1),
Direction::Backward => Duration::seconds(-1),
}).ok_or(CronError::InvalidTime).ok()?;
}
// Finally, return the found_time for the current iteration.
// This `found_time` is the original value received from `find_occurrence`.
Some(found_time)
}
Err(CronError::TimeSearchLimitExceeded) => None,
Err(e) => {
eprintln!("CronIterator encountered an error: {e:?}");
None
}
}
}
}