rx_rust/operators/transforming/window.rs
1use crate::{
2 disposable::Disposable,
3 observable::{Observable, Subscription},
4 observer::{Flow, Observer, Termination},
5 subject::unicast_subject::{UnicastObservable, UnicastSender, unicast_subject},
6 utils::{
7 pending_events::EventBatch,
8 subscribe_with_context::{self, SubscriptionContext, subscribe_with_context_owning_source},
9 types::MaybeSend,
10 },
11};
12use educe::Educe;
13
14/// Periodically subdivides items from an Observable into Observable windows.
15///
16/// A new window is emitted whenever the `boundary` Observable emits an item.
17/// Completing the `boundary` stops future window rotation without terminating the current window
18/// or the outer Observable.
19/// An error from the `boundary` terminates the current window and the outer Observable.
20///
21/// Each window is a single-consumer pipe: it can be subscribed to once, it buffers the items that
22/// arrive while it has no subscriber, and dropping it without subscribing discards its items. A
23/// window is serialized on its own rather than together with the outer Observable, so the events
24/// of a window keep their order among themselves, but they are not ordered against the emission of
25/// a later window. Disposing the outer subscription drops the observer of the open window without
26/// notifying it.
27///
28/// Disposing the subscription of a single window does not necessarily release its observer where
29/// it happens: the window releases it on its next item, when it ends, or when the outer
30/// subscription is disposed, whichever comes first. See
31/// [the unicast subject](crate::subject::unicast_subject) each window is built on.
32/// See <https://reactivex.io/documentation/operators/window.html>
33///
34/// # Examples
35/// ```rust
36/// use rx_rust::{
37/// observable::ObservableExt,
38/// observer::{Observer, Termination},
39/// operators::transforming::window::Window,
40/// subject::publish_subject::PublishSubject,
41/// };
42/// use std::{convert::Infallible, sync::{Arc, Mutex}};
43///
44/// let windows = Arc::new(Mutex::new(Vec::<Vec<i32>>::new()));
45/// let terminations = Arc::new(Mutex::new(Vec::new()));
46/// let inner_subscriptions = Arc::new(Mutex::new(Vec::new()));
47///
48/// let mut source: PublishSubject<'_, i32, Infallible> = PublishSubject::default();
49/// let mut boundary: PublishSubject<'_, (), Infallible> = PublishSubject::default();
50/// let windows_observer = Arc::clone(&windows);
51/// let terminations_observer = Arc::clone(&terminations);
52/// let inner_subscriptions_observer = Arc::clone(&inner_subscriptions);
53///
54/// let subscription = Window::new(source.clone(), boundary.clone()).subscribe_with_callback(
55/// move |window| {
56/// let index = {
57/// let mut windows = windows_observer.lock().unwrap();
58/// windows.push(Vec::new());
59/// windows.len() - 1
60/// };
61/// let windows_for_values = Arc::clone(&windows_observer);
62/// let sub = window.subscribe_with_callback(
63/// move |value| {
64/// windows_for_values.lock().unwrap()[index].push(value);
65/// },
66/// |_| {},
67/// );
68/// inner_subscriptions_observer.lock().unwrap().push(sub);
69/// },
70/// move |termination| terminations_observer
71/// .lock()
72/// .unwrap()
73/// .push(termination),
74/// );
75///
76/// source.on_next(1);
77/// source.on_next(2);
78/// boundary.on_next(());
79/// source.on_next(3);
80/// source.on_termination(Termination::Completed);
81/// drop(subscription);
82/// inner_subscriptions.lock().unwrap().drain(..).for_each(drop);
83///
84/// assert_eq!(
85/// &*windows.lock().unwrap(),
86/// &[vec![1, 2], vec![3]]
87/// );
88/// assert_eq!(
89/// &*terminations.lock().unwrap(),
90/// &[Termination::Completed]
91/// );
92/// ```
93#[derive(Educe)]
94#[educe(Debug, Clone)]
95pub struct Window<OE, OE1> {
96 source: OE,
97 boundary: OE1,
98}
99
100impl<OE, OE1> Window<OE, OE1> {
101 pub fn new<'or, T, E>(source: OE, boundary: OE1) -> Self
102 where
103 OE: Observable<'or, T, E>,
104 OE1: Observable<'or, (), E>,
105 {
106 Self { source, boundary }
107 }
108}
109
110impl<'or, T, E, OE, OE1> Observable<'or, UnicastObservable<'or, T, E>, E> for Window<OE, OE1>
111where
112 T: MaybeSend + 'or,
113 E: Clone + MaybeSend + 'or,
114 OE: Observable<'or, T, E>,
115 OE::D: MaybeSend + 'or,
116 OE1: Observable<'or, (), E>,
117 OE1::D: MaybeSend + 'or,
118{
119 type D = subscribe_with_context::OwningDisposal<'or>;
120
121 fn subscribe(
122 self,
123 observer: impl Observer<UnicastObservable<'or, T, E>, E> + MaybeSend + 'or,
124 ) -> Subscription<Self::D> {
125 let observer = DelegateObserver {
126 outer_observer: observer,
127 sender: None,
128 };
129 // The windows own their buffered items, so the context needs no model of its own: it only
130 // serializes the actions below and owns the source and boundary subscriptions.
131 subscribe_with_context_owning_source(observer, (), |context| {
132 // The first window is opened before subscribing, so that a synchronous source has a
133 // window to deliver its values to. An observer that stops on that first window stops
134 // the context, which disposes the subscriptions below as soon as they are installed.
135 let _ = context.send_next(DelegateAction::EmitWindow);
136 let boundary_subscription = self.boundary.subscribe(BoundaryObserver(context.clone()));
137 let source_subscription = self.source.subscribe(SourceObserver(context));
138 boundary_subscription.preceded_by_bound(source_subscription)
139 })
140 }
141}
142
143type WindowContext<'or, T, E, OR, D> =
144 SubscriptionContext<DelegateAction<T, E>, E, DelegateObserver<'or, T, E, OR>, (), D>;
145
146struct SourceObserver<'or, T, E, OR, D: Disposable>(WindowContext<'or, T, E, OR, D>);
147
148impl<'or, T, E, OR, D> Observer<T, E> for SourceObserver<'or, T, E, OR, D>
149where
150 E: Clone,
151 OR: Observer<UnicastObservable<'or, T, E>, E>,
152 D: Disposable,
153{
154 fn on_next(&mut self, value: T) -> Flow {
155 // The value is queued as an action, so that it reaches the window outside the lock.
156 self.0.send_next(DelegateAction::ForwardValue(value))
157 }
158
159 fn on_termination(self, termination: Termination<E>) {
160 terminate(self.0, termination);
161 }
162}
163
164/// Terminates the current window, then the outer Observable.
165fn terminate<'or, T, E, OR, D>(
166 context: WindowContext<'or, T, E, OR, D>,
167 termination: Termination<E>,
168) where
169 E: Clone,
170 OR: Observer<UnicastObservable<'or, T, E>, E>,
171 D: Disposable,
172{
173 let _ = context.send(EventBatch::NextAndTermination(
174 DelegateAction::TerminateWindow(termination.clone()),
175 termination,
176 ));
177}
178
179struct BoundaryObserver<'or, T, E, OR, D: Disposable>(WindowContext<'or, T, E, OR, D>);
180
181impl<'or, T, E, OR, D> Observer<(), E> for BoundaryObserver<'or, T, E, OR, D>
182where
183 E: Clone,
184 OR: Observer<UnicastObservable<'or, T, E>, E>,
185 D: Disposable,
186{
187 fn on_next(&mut self, _: ()) -> Flow {
188 // Two events rather than one, so that disposing the outer subscription while the current
189 // window is completing suppresses the new window.
190 self.0.send(EventBatch::NextBatch(vec![
191 DelegateAction::TerminateWindow(Termination::Completed),
192 DelegateAction::EmitWindow,
193 ]))
194 }
195
196 fn on_termination(self, termination: Termination<E>) {
197 // Completing the boundary only stops the rotation: the current window and the outer
198 // Observable keep going.
199 if let error @ Termination::Error(_) = termination {
200 terminate(self.0, error);
201 }
202 }
203}
204
205enum DelegateAction<T, E> {
206 /// Sends a source value to the current window, if there is one.
207 ForwardValue(T),
208 /// Terminates the current window, if there is one.
209 TerminateWindow(Termination<E>),
210 /// Opens a new window, makes it the current one and emits it downstream.
211 EmitWindow,
212}
213
214/// Owns the state that the actions act on, so that a window is fed and emitted outside the lock of
215/// the context that serializes the source against the boundary.
216struct DelegateObserver<'or, T, E, OR> {
217 outer_observer: OR,
218 /// The sending end of the window that is currently open, which is the only place where the
219 /// windows are fed from. It is `None` until the first window opens and after the last window
220 /// ended.
221 sender: Option<UnicastSender<'or, T, E>>,
222}
223
224impl<'or, T, E, OR> Observer<DelegateAction<T, E>, E> for DelegateObserver<'or, T, E, OR>
225where
226 OR: Observer<UnicastObservable<'or, T, E>, E>,
227{
228 fn on_next(&mut self, action: DelegateAction<T, E>) -> Flow {
229 match action {
230 DelegateAction::ForwardValue(value) => {
231 match &mut self.sender {
232 // The consumer of one window stops that window, not the operator: the next
233 // window has its own consumer.
234 Some(sender) => {
235 let _ = sender.on_next(value);
236 }
237 // No window accepts values, so this one has nowhere to go.
238 None => drop(value),
239 }
240 Flow::Continue
241 }
242 DelegateAction::TerminateWindow(termination) => {
243 if let Some(sender) = self.sender.take() {
244 sender.on_termination(termination);
245 }
246 Flow::Continue
247 }
248 DelegateAction::EmitWindow => {
249 debug_assert!(self.sender.is_none());
250 let (sender, window) = unicast_subject();
251 self.sender = Some(sender);
252 self.outer_observer.on_next(window)
253 }
254 }
255 }
256
257 fn on_termination(self, termination: Termination<E>) {
258 // The current window is terminated by the `TerminateWindow` action that precedes this one.
259 debug_assert!(self.sender.is_none());
260 self.outer_observer.on_termination(termination);
261 }
262}