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}