sqlx_core/ext/
async_stream.rs1use std::future::Future;
7use std::pin::Pin;
8use std::sync::{Arc, Mutex};
9use std::task::{Context, Poll};
10
11use futures_core::future::BoxFuture;
12use futures_core::stream::Stream;
13use futures_core::FusedFuture;
14use futures_util::future::Fuse;
15use futures_util::FutureExt;
16
17use crate::error::Error;
18
19pub struct TryAsyncStream<'a, T> {
20 yielder: Yielder<T>,
21 future: Fuse<BoxFuture<'a, Result<(), Error>>>,
22}
23
24impl<'a, T> TryAsyncStream<'a, T> {
25 pub fn new<F, Fut>(f: F) -> Self
26 where
27 F: FnOnce(Yielder<T>) -> Fut + Send,
28 Fut: 'a + Future<Output = Result<(), Error>> + Send,
29 T: 'a + Send,
30 {
31 let yielder = Yielder::new();
32
33 let future = f(yielder.duplicate()).boxed().fuse();
34
35 Self { future, yielder }
36 }
37}
38
39pub struct Yielder<T> {
40 value: Arc<Mutex<Option<T>>>,
43}
44
45impl<T> Yielder<T> {
46 fn new() -> Self {
47 Yielder {
48 value: Arc::new(Mutex::new(None)),
49 }
50 }
51
52 fn duplicate(&self) -> Self {
54 Yielder {
55 value: self.value.clone(),
56 }
57 }
58
59 pub async fn r#yield(&self, val: T) {
61 let replaced = self
62 .value
63 .lock()
64 .expect("BUG: panicked while holding a lock")
65 .replace(val);
66
67 debug_assert!(
68 replaced.is_none(),
69 "BUG: previously yielded value not taken"
70 );
71
72 let mut yielded = false;
73
74 futures_util::future::poll_fn(|_cx| {
80 if !yielded {
81 yielded = true;
82 Poll::Pending
83 } else {
84 Poll::Ready(())
85 }
86 })
87 .await
88 }
89
90 fn take(&self) -> Option<T> {
91 self.value
92 .lock()
93 .expect("BUG: panicked while holding a lock")
94 .take()
95 }
96}
97
98impl<'a, T> Stream for TryAsyncStream<'a, T> {
99 type Item = Result<T, Error>;
100
101 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
102 if self.future.is_terminated() {
103 return Poll::Ready(None);
104 }
105
106 match self.future.poll_unpin(cx) {
107 Poll::Ready(Ok(())) => {
108 Poll::Ready(None)
111 }
112 Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
113 Poll::Pending => self
114 .yielder
115 .take()
116 .map_or(Poll::Pending, |val| Poll::Ready(Some(Ok(val)))),
117 }
118 }
119}
120
121#[macro_export]
122macro_rules! try_stream {
123 ($($block:tt)*) => {
124 $crate::ext::async_stream::TryAsyncStream::new(move |yielder| async move {
125 let yielder = &yielder;
128
129 macro_rules! r#yield {
130 ($v:expr) => {{
131 yielder.r#yield($v).await;
132 }}
133 }
134
135 $($block)*
136 })
137 }
138}