Skip to main content

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}