1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
use super::assert_stream;
use core::fmt;
use core::pin::Pin;
use futures_core::future::TryFuture;
use futures_core::ready;
use futures_core::stream::Stream;
use futures_core::task::{Context, Poll};
use pin_project_lite::pin_project;
/// Creates a `TryStream` from a seed and a closure returning a `TryFuture`.
///
/// This function is the dual for the `TryStream::try_fold()` adapter: while
/// `TryStream::try_fold()` reduces a `TryStream` to one single value,
/// `try_unfold()` creates a `TryStream` from a seed value.
///
/// `try_unfold()` will call the provided closure with the provided seed, then
/// wait for the returned `TryFuture` to complete with `(a, b)`. It will then
/// yield the value `a`, and use `b` as the next internal state.
///
/// If the closure returns `None` instead of `Some(TryFuture)`, then the
/// `try_unfold()` will stop producing items and return `Poll::Ready(None)` in
/// future calls to `poll()`.
///
/// In case of error generated by the returned `TryFuture`, the error will be
/// returned by the `TryStream`. The `TryStream` will then yield
/// `Poll::Ready(None)` in future calls to `poll()`.
///
/// This function can typically be used when wanting to go from the "world of
/// futures" to the "world of streams": the provided closure can build a
/// `TryFuture` using other library functions working on futures, and
/// `try_unfold()` will turn it into a `TryStream` by repeating the operation.
///
/// # Example
///
/// ```
/// # #[derive(Debug, PartialEq)]
/// # struct SomeError;
/// # futures::executor::block_on(async {
/// use futures::stream::{self, TryStreamExt};
///
/// let stream = stream::try_unfold(0, |state| async move {
///     if state < 0 {
///         return Err(SomeError);
///     }
///
///     if state <= 2 {
///         let next_state = state + 1;
///         let yielded = state * 2;
///         Ok(Some((yielded, next_state)))
///     } else {
///         Ok(None)
///     }
/// });
///
/// let result: Result<Vec<i32>, _> = stream.try_collect().await;
/// assert_eq!(result, Ok(vec![0, 2, 4]));
/// # });
/// ```
pub fn try_unfold<T, F, Fut, Item>(init: T, f: F) -> TryUnfold<T, F, Fut>
where
    F: FnMut(T) -> Fut,
    Fut: TryFuture<Ok = Option<(Item, T)>>,
{
    assert_stream::<Result<Item, Fut::Error>, _>(TryUnfold { f, state: Some(init), fut: None })
}
pin_project! {
    /// Stream for the [`try_unfold`] function.
    #[must_use = "streams do nothing unless polled"]
    pub struct TryUnfold<T, F, Fut> {
        f: F,
        state: Option<T>,
        #[pin]
        fut: Option<Fut>,
    }
}
impl<T, F, Fut> fmt::Debug for TryUnfold<T, F, Fut>
where
    T: fmt::Debug,
    Fut: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TryUnfold").field("state", &self.state).field("fut", &self.fut).finish()
    }
}
impl<T, F, Fut, Item> Stream for TryUnfold<T, F, Fut>
where
    F: FnMut(T) -> Fut,
    Fut: TryFuture<Ok = Option<(Item, T)>>,
{
    type Item = Result<Item, Fut::Error>;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut this = self.project();
        if let Some(state) = this.state.take() {
            this.fut.set(Some((this.f)(state)));
        }
        match this.fut.as_mut().as_pin_mut() {
            None => {
                // The future previously errored
                Poll::Ready(None)
            }
            Some(future) => {
                let step = ready!(future.try_poll(cx));
                this.fut.set(None);
                match step {
                    Ok(Some((item, next_state))) => {
                        *this.state = Some(next_state);
                        Poll::Ready(Some(Ok(item)))
                    }
                    Ok(None) => Poll::Ready(None),
                    Err(e) => Poll::Ready(Some(Err(e))),
                }
            }
        }
    }
}