Skip to main content

rx_rust/operators/others/
observable_try_stream.rs

1use crate::{
2    observable::{Observable, Subscription},
3    observer::{Flow, Observer, Termination},
4    utils::mutable::{Mutable, MutableHelper},
5    utils::types::{MaybeSend, Shared},
6};
7use educe::Educe;
8use futures::Stream;
9use std::{
10    collections::VecDeque,
11    num::NonZeroUsize,
12    task::{Poll, Waker},
13};
14
15#[derive(Educe)]
16#[educe(Debug)]
17struct ObservableTryStreamContext<E, B> {
18    buffer: B,
19    waker: Option<Waker>,
20    termination: Option<Termination<E>>,
21}
22
23/// Converts a fallible Observable into a `futures::Stream` of `Result`s that can be used with
24/// `async/await` and `futures::TryStreamExt`.
25///
26/// Each item becomes `Ok(item)`. An error from the source becomes the last item, `Err(error)`,
27/// after which the stream ends; completion ends the stream without an item. A source that cannot
28/// fail goes through
29/// [`ObservableStream`](crate::operators::others::observable_stream::ObservableStream) instead.
30///
31/// Like any stream, it does nothing until it is polled: that first poll is what subscribes to the
32/// source. Items are buffered until they are polled, so the stream never stops the source itself;
33/// dropping the stream disposes the subscription instead. The buffer is a [`StreamBuffer`]:
34/// [`Unbounded`] by default, which keeps everything, or the one handed to
35/// [`with_buffer`](Self::with_buffer) — see there for what a source faster than the consumer
36/// costs and how to bound it.
37///
38/// # Examples
39/// ```rust
40/// use futures::TryStreamExt;
41/// use rx_rust::{
42///     observable::ObservableExt,
43///     operators::creating::{from_iter::FromIter, throw::Throw},
44/// };
45///
46/// futures::executor::block_on(async {
47///     let source = FromIter::new(vec![1, 2, 3]).with_error_type::<&str>();
48///     let values: Result<Vec<_>, _> = source.into_try_stream().try_collect().await;
49///     assert_eq!(values, Ok(vec![1, 2, 3]));
50///
51///     let source = FromIter::new(vec![1, 2])
52///         .with_error_type()
53///         .concat_with(Throw::new("boom").with_item_type());
54///     let values: Result<Vec<_>, _> = source.into_try_stream().try_collect().await;
55///     assert_eq!(values, Err("boom"));
56/// });
57/// ```
58#[derive(Educe)]
59#[educe(Debug)]
60pub struct ObservableTryStream<'or, T, E, OE, B = Unbounded<T>>
61where
62    OE: Observable<'or, T, E>,
63{
64    source: Option<OE>,
65    sub: Option<Subscription<OE::D>>,
66    context: Shared<Mutable<ObservableTryStreamContext<E, B>>>,
67}
68
69impl<'or, T, E, OE> ObservableTryStream<'or, T, E, OE>
70where
71    OE: Observable<'or, T, E>,
72{
73    /// Buffers every item until it is polled; see [`Unbounded`].
74    pub fn new(source: OE) -> Self {
75        Self::with_buffer(source, Unbounded::new())
76    }
77}
78
79impl<'or, T, E, OE, B> ObservableTryStream<'or, T, E, OE, B>
80where
81    OE: Observable<'or, T, E>,
82    B: StreamBuffer<T>,
83{
84    /// Keeps the items that arrive between two polls in `buffer`, which decides what a source
85    /// faster than the consumer costs: [`Unbounded`] keeps everything,
86    /// [`Latest`] only the newest item and
87    /// [`Bounded`] a fixed number of them.
88    pub fn with_buffer(source: OE, buffer: B) -> Self {
89        Self {
90            source: Some(source),
91            sub: None,
92            context: Shared::new(Mutable::new(ObservableTryStreamContext {
93                buffer,
94                waker: None,
95                termination: None,
96            })),
97        }
98    }
99}
100
101impl<'or, T, E, OE, B> Unpin for ObservableTryStream<'or, T, E, OE, B> where
102    OE: Observable<'or, T, E>
103{
104}
105
106impl<'or, T, E, OE, B> Stream for ObservableTryStream<'or, T, E, OE, B>
107where
108    T: MaybeSend + 'or,
109    E: MaybeSend + 'or,
110    OE: Observable<'or, T, E>,
111    B: StreamBuffer<T> + MaybeSend + 'or,
112{
113    type Item = Result<B::Item, E>;
114
115    fn poll_next(
116        mut self: std::pin::Pin<&mut Self>,
117        cx: &mut std::task::Context<'_>,
118    ) -> Poll<Option<Self::Item>> {
119        if let Some(source) = self.source.take() {
120            let observer = ObservableTryStreamObserver {
121                context: self.context.clone(),
122            };
123            let sub = source.subscribe(observer);
124            self.sub = Some(sub);
125        }
126
127        let waker = cx.waker().clone();
128        // The waker this one replaces is handed back, because dropping a `Waker` runs the
129        // external code of its vtable, which must not run under the lock.
130        let (poll, previous_waker) = self.context.with_mut(|context| {
131            let previous_waker = context.waker.replace(waker);
132            let poll = if let Some(value) = context.buffer.pop() {
133                Poll::Ready(Some(Ok(value)))
134            } else {
135                match context.termination.take() {
136                    None => Poll::Pending,
137                    Some(termination) => {
138                        // An error is handed out once, as the last item; from then on the
139                        // stream ends the way a completed one does.
140                        context.termination = Some(Termination::Completed);
141                        match termination {
142                            Termination::Error(error) => Poll::Ready(Some(Err(error))),
143                            Termination::Completed => Poll::Ready(None),
144                        }
145                    }
146                }
147            };
148            (poll, previous_waker)
149        });
150        drop(previous_waker); // Drop outside the lock to avoid potential deadlock
151        poll
152    }
153}
154
155struct ObservableTryStreamObserver<E, B> {
156    context: Shared<Mutable<ObservableTryStreamContext<E, B>>>,
157}
158
159impl<T, E, B> Observer<T, E> for ObservableTryStreamObserver<E, B>
160where
161    B: StreamBuffer<T>,
162{
163    fn on_next(&mut self, value: T) -> Flow {
164        // The waker is taken under the lock and woken after it is released, because waking runs
165        // external code, which must not run under the lock. The item the buffer evicts to make
166        // room is dropped outside it for the same reason.
167        let (evicted, waker) = self
168            .context
169            .with_mut(|context| (context.buffer.push(value), context.waker.take()));
170        drop(evicted);
171        if let Some(waker) = waker {
172            waker.wake();
173        }
174        // The stream buffers whatever arrives, so it never stops the source itself: dropping the
175        // stream disposes the subscription instead.
176        Flow::Continue
177    }
178
179    fn on_termination(self, termination: Termination<E>) {
180        // The waker is woken outside the lock, like in `on_next`. The termination this one
181        // replaces is dropped outside it too; there is none unless the source breaks its
182        // contract, since a terminated observer receives nothing more.
183        let (waker, replaced) = self.context.with_mut(|context| {
184            (
185                context.waker.take(),
186                context.termination.replace(termination),
187            )
188        });
189        drop(replaced);
190        if let Some(waker) = waker {
191            waker.wake();
192        }
193    }
194}
195
196/// Decides what [`into_stream_with`](crate::observable::ObservableExt::into_stream_with) keeps
197/// when the source pushes faster than the stream is polled.
198///
199/// An observable pushes at its own pace while a `Stream` hands out one item per poll, so the
200/// items that arrive between two polls have to go somewhere. The buffer is where: every item
201/// the source pushes goes through [`push`](Self::push), and every poll takes the next item out
202/// with [`pop`](Self::pop). A `Stream` cannot slow its source down, so the buffer alone
203/// decides what survives — and at what cost in memory — when the consumer falls behind.
204///
205/// Three buffers come with the crate: [`Unbounded`] keeps everything, [`Latest`] keeps the
206/// newest item only, and [`Bounded`] keeps a fixed number of items and drops the oldest or the
207/// newest beyond that. An implementation of your own can also fold the items that pile up into
208/// one, which is why the item the stream yields ([`Item`](Self::Item)) need not be the item
209/// the source pushes.
210///
211/// # Examples
212/// A buffer that adds up the numbers that arrive between two polls:
213/// ```rust
214/// use futures::{FutureExt, StreamExt};
215/// use rx_rust::{
216///     observable::ObservableExt, observer::Observer,
217///     operators::others::observable_try_stream::StreamBuffer,
218///     subject::publish_subject::PublishSubject,
219/// };
220/// use std::convert::Infallible;
221///
222/// #[derive(Default)]
223/// struct Sum(Option<i32>);
224///
225/// impl StreamBuffer<i32> for Sum {
226///     type Item = i32;
227///
228///     fn push(&mut self, item: i32) -> Option<i32> {
229///         *self.0.get_or_insert(0) += item;
230///         None
231///     }
232///
233///     fn pop(&mut self) -> Option<i32> {
234///         self.0.take()
235///     }
236/// }
237///
238/// let mut subject = PublishSubject::<_, Infallible>::new();
239/// let mut stream = subject.clone().into_stream_with(Sum::default());
240/// assert_eq!(stream.next().now_or_never(), None); // subscribes
241///
242/// subject.on_next(1);
243/// subject.on_next(2);
244/// subject.on_next(3);
245/// assert_eq!(stream.next().now_or_never(), Some(Some(6)));
246/// ```
247pub trait StreamBuffer<T> {
248    /// What the stream yields.
249    type Item;
250
251    /// Stores an item the source pushed.
252    ///
253    /// Returns the item that had to go to make room, if any, so that it is dropped outside the
254    /// lock the buffer lives under.
255    fn push(&mut self, item: T) -> Option<T>;
256
257    /// Takes the next item to yield, or `None` when the stream has to wait for the source.
258    fn pop(&mut self) -> Option<Self::Item>;
259}
260
261/// Keeps every item, in order. This is what [`into_stream`](crate::observable::ObservableExt::into_stream) uses.
262///
263/// Nothing is ever dropped, so a source faster than the consumer grows the buffer without bound.
264#[derive(Educe)]
265#[educe(Debug, Default)]
266pub struct Unbounded<T>(VecDeque<T>);
267
268impl<T> Unbounded<T> {
269    pub fn new() -> Self {
270        Self(VecDeque::new())
271    }
272}
273
274impl<T> StreamBuffer<T> for Unbounded<T> {
275    type Item = T;
276
277    fn push(&mut self, item: T) -> Option<T> {
278        self.0.push_back(item);
279        None
280    }
281
282    fn pop(&mut self) -> Option<T> {
283        self.0.pop_front()
284    }
285}
286
287/// Keeps only the newest item: each item the source pushes replaces the one waiting to be
288/// polled. This is `onBackpressureLatest` of other ReactiveX stacks.
289///
290/// The memory cost is one item whatever the pace of the source, at the price of skipping the
291/// items the consumer was too slow to see — right for a stream of states, where only the
292/// current one matters, and wrong for a stream of events.
293#[derive(Educe)]
294#[educe(Debug, Default)]
295pub struct Latest<T>(Option<T>);
296
297impl<T> Latest<T> {
298    pub fn new() -> Self {
299        Self(None)
300    }
301}
302
303impl<T> StreamBuffer<T> for Latest<T> {
304    type Item = T;
305
306    fn push(&mut self, item: T) -> Option<T> {
307        self.0.replace(item)
308    }
309
310    fn pop(&mut self) -> Option<T> {
311        self.0.take()
312    }
313}
314
315/// What [`Bounded`] does with an item that arrives when the buffer is full.
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum Overflow {
318    /// The oldest waiting item makes room for the new one, so the buffer holds the newest
319    /// items.
320    DropOldest,
321    /// The new item is dropped, so the buffer holds the oldest items.
322    DropNewest,
323}
324
325/// Keeps at most `capacity` items, in order, and applies an [`Overflow`] rule beyond that. This
326/// is `onBackpressureBuffer` with a capacity of other ReactiveX stacks.
327///
328/// The capacity is a [`NonZeroUsize`]: a buffer that holds nothing would yield nothing.
329/// [`Latest`] is `Bounded::drop_oldest` with a capacity of one.
330#[derive(Educe)]
331#[educe(Debug)]
332pub struct Bounded<T> {
333    capacity: NonZeroUsize,
334    overflow: Overflow,
335    values: VecDeque<T>,
336}
337
338impl<T> Bounded<T> {
339    /// Keeps at most `capacity` items and applies `overflow` beyond that.
340    pub fn new(capacity: NonZeroUsize, overflow: Overflow) -> Self {
341        Self {
342            capacity,
343            overflow,
344            values: VecDeque::with_capacity(capacity.get()),
345        }
346    }
347
348    /// Keeps the newest `capacity` items; see [`Overflow::DropOldest`].
349    pub fn drop_oldest(capacity: NonZeroUsize) -> Self {
350        Self::new(capacity, Overflow::DropOldest)
351    }
352
353    /// Keeps the oldest `capacity` items; see [`Overflow::DropNewest`].
354    pub fn drop_newest(capacity: NonZeroUsize) -> Self {
355        Self::new(capacity, Overflow::DropNewest)
356    }
357}
358
359impl<T> StreamBuffer<T> for Bounded<T> {
360    type Item = T;
361
362    fn push(&mut self, item: T) -> Option<T> {
363        if self.values.len() < self.capacity.get() {
364            self.values.push_back(item);
365            return None;
366        }
367        match self.overflow {
368            Overflow::DropOldest => {
369                let evicted = self.values.pop_front();
370                self.values.push_back(item);
371                evicted
372            }
373            Overflow::DropNewest => Some(item),
374        }
375    }
376
377    fn pop(&mut self) -> Option<T> {
378        self.values.pop_front()
379    }
380}