timewindow/iter.rs
1use chrono::{DateTime, Utc};
2
3use crate::{Window, WindowSource};
4
5/// An iterator over successive upcoming windows from a [`WindowSource`].
6///
7/// This iterator repeatedly calls [`WindowSource::next_window`] and yields the
8/// returned windows in ascending start-time order.
9///
10/// # Progress semantics
11///
12/// After yielding a window, the iterator advances its internal cursor to that
13/// window's `start`, not its `end`. This is intentional: sources may produce
14/// overlapping windows, and advancing by `end` could skip valid future windows
15/// that start during an earlier yielded window.
16///
17/// Implementations of [`WindowSource::next_window`] are required to return a
18/// window whose `start` is strictly greater than the supplied cursor. As a
19/// defensive safeguard, this iterator terminates if a source returns a
20/// non-progressing window.
21///
22/// # Termination
23///
24/// The iterator ends when the source returns `None`, or when the source
25/// violates the strict-progress contract.
26pub struct NextWindows<'a, S>
27where
28 S: WindowSource,
29{
30 source: &'a S,
31 cursor: DateTime<Utc>,
32}
33
34impl<'a, S> NextWindows<'a, S>
35where
36 S: WindowSource,
37{
38 /// Creates an iterator over windows starting strictly after `from`.
39 pub fn new(source: &'a S, from: DateTime<Utc>) -> Self {
40 Self {
41 source,
42 cursor: from,
43 }
44 }
45}
46
47impl<'a, S> Iterator for NextWindows<'a, S>
48where
49 S: WindowSource,
50{
51 type Item = Window<S::Meta>;
52
53 fn next(&mut self) -> Option<Self::Item> {
54 let window = self.source.next_window(self.cursor)?;
55 if window.start <= self.cursor {
56 return None;
57 }
58 self.cursor = window.start;
59 Some(window)
60 }
61}
62
63/// Extension methods for [`WindowSource`].
64///
65/// This trait provides convenience iteration helpers for any window source.
66pub trait WindowSourceExt: WindowSource {
67 /// Returns an iterator over successive windows after `from`.
68 ///
69 /// Each yielded window is produced by repeated calls to
70 /// [`WindowSource::next_window`]. The iterator preserves overlap-aware
71 /// semantics by advancing from one yielded window's `start` to the next.
72 ///
73 /// For infinite recurring sources, this iterator may itself be infinite;
74 /// callers can combine it with adapters like [`Iterator::take`].
75 fn next_windows_from(&self, from: DateTime<Utc>) -> NextWindows<'_, Self>
76 where
77 Self: Sized,
78 {
79 NextWindows::new(self, from)
80 }
81}
82
83impl<T: WindowSource> WindowSourceExt for T {}