Skip to content

Commit 15aa74a

Browse files
committed
Move public entry point functions to beginning of module
1 parent ae494ba commit 15aa74a

1 file changed

Lines changed: 110 additions & 106 deletions

File tree

datafusion/execution/src/async_stream.rs

Lines changed: 110 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,129 @@ use std::sync::{Arc};
2525
use std::task::{Context, Poll};
2626
use parking_lot::Mutex;
2727

28+
/// Creates a [`Stream`] from an async generator function.
29+
///
30+
/// The `generator` closure receives an [`Emitter<T>`] and runs as an async
31+
/// block. Each `emitter.emit(value).await` call suspends the generator and
32+
/// produces the next item in the stream. The stream ends when the generator
33+
/// future resolves.
34+
///
35+
/// # Example
36+
///
37+
/// ```
38+
/// use datafusion_execution::async_stream;
39+
/// use futures::StreamExt;
40+
///
41+
/// # #[tokio::main(flavor = "current_thread")]
42+
/// # async fn main() {
43+
/// let stream = async_stream(|mut emitter| async move {
44+
/// for i in 0_i32..3 {
45+
/// emitter.emit(i).await;
46+
/// }
47+
/// });
48+
///
49+
/// let values: Vec<i32> = stream.collect().await;
50+
/// assert_eq!(values, vec![0, 1, 2]);
51+
/// # }
52+
/// ```
53+
pub fn async_stream<T, F: Future<Output = ()>>(
54+
generator: impl FnOnce(Emitter<T>) -> F,
55+
) -> impl FusedStream<Item = T> {
56+
let (emitter, receiver) = tx_rx();
57+
AsyncStream::new(receiver, generator(emitter))
58+
}
59+
60+
/// Creates a fallible [`Stream`] from an async generator function.
61+
///
62+
/// The `generator` closure receives a [`TryEmitter<T, E>`] and runs as an
63+
/// async block that returns `Result<(), E>`. Each `emitter.emit(value).await`
64+
/// call suspends the generator and produces `Ok(value)` as the next stream
65+
/// item. The `?` operator can be used inside the generator to short-circuit on
66+
/// errors: the error is emitted as the final `Err(e)` item and the stream
67+
/// ends. The stream also ends when the generator future resolves to `Ok(())`.
68+
///
69+
/// # Example
70+
///
71+
/// ```
72+
/// use datafusion_execution::async_try_stream;
73+
/// use futures::StreamExt;
74+
///
75+
/// # #[tokio::main(flavor = "current_thread")]
76+
/// # async fn main() {
77+
/// let stream = async_try_stream(|mut emitter| async move {
78+
/// emitter.emit(1_i32).await;
79+
/// emitter.emit(2_i32).await;
80+
/// Err::<(), _>("something went wrong")?;
81+
/// emitter.emit(3_i32).await; // never reached
82+
/// Ok(())
83+
/// });
84+
///
85+
/// let values: Vec<Result<i32, &str>> = stream.collect().await;
86+
/// assert_eq!(values, vec![Ok(1), Ok(2), Err("something went wrong")]);
87+
/// # }
88+
/// ```
89+
pub fn async_try_stream<T, E, F: Future<Output = Result<(), E>>>(
90+
generator: impl FnOnce(TryEmitter<T, E>) -> F,
91+
) -> impl FusedStream<Item = Result<T, E>> {
92+
let (try_emitter, mut emitter, receiver) = try_tx_rx::<T, E>();
93+
AsyncStream::new(receiver, async move {
94+
if let Err(e) = generator(try_emitter).await {
95+
emitter.emit(Err(e)).await
96+
}
97+
})
98+
}
99+
100+
/// Creates an `Emitter`/`Receiver` pair
101+
fn tx_rx<T>() -> (Emitter<T>, Receiver<T>) {
102+
let slot = Arc::new(Mutex::new(None));
103+
(
104+
Emitter {
105+
slot: Arc::clone(&slot),
106+
},
107+
Receiver { slot },
108+
)
109+
}
110+
111+
/// Creates an `TryEmitter`/`Emitter`/`Receiver` triplet
112+
#[expect(
113+
clippy::type_complexity,
114+
reason = "three-element tuple is clearer than an alias here"
115+
)]
116+
fn try_tx_rx<T, E>() -> (
117+
TryEmitter<T, E>,
118+
Emitter<Result<T, E>>,
119+
Receiver<Result<T, E>>,
120+
) {
121+
let slot = Arc::new(Mutex::new(None));
122+
(
123+
TryEmitter {
124+
slot: Arc::clone(&slot),
125+
},
126+
Emitter {
127+
slot: Arc::clone(&slot),
128+
},
129+
Receiver { slot },
130+
)
131+
}
132+
133+
type SlotRef<T> = Arc<Mutex<Option<T>>>;
134+
28135
/// A handle for emitting values from an [`async_stream`] generator.
29136
///
30137
/// The generator closure receives an `Emitter<T>` as its argument.
31138
pub struct Emitter<T> {
32-
slot: Arc<Mutex<Option<T>>>,
139+
slot: SlotRef<T>,
33140
}
34141

35142
/// A handle for emitting values from an [`async_try_stream`] generator.
36143
///
37144
/// The generator closure receives a `TryEmitter<T, E>` as its argument.
38145
pub struct TryEmitter<T, E> {
39-
slot: Arc<Mutex<Option<Result<T, E>>>>,
146+
slot: SlotRef<T>,
40147
}
41148

42149
struct Receiver<T> {
43-
slot: Arc<Mutex<Option<T>>>,
150+
slot: SlotRef<T>,
44151
}
45152

46153
impl<T> Emitter<T> {
@@ -177,109 +284,6 @@ where
177284
}
178285
}
179286

180-
fn tx_rx<T>() -> (Emitter<T>, Receiver<T>) {
181-
let slot = Arc::new(Mutex::new(None));
182-
(
183-
Emitter {
184-
slot: Arc::clone(&slot),
185-
},
186-
Receiver { slot },
187-
)
188-
}
189-
190-
/// Creates a [`Stream`] from an async generator function.
191-
///
192-
/// The `generator` closure receives an [`Emitter<T>`] and runs as an async
193-
/// block. Each `emitter.emit(value).await` call suspends the generator and
194-
/// produces the next item in the stream. The stream ends when the generator
195-
/// future resolves.
196-
///
197-
/// # Example
198-
///
199-
/// ```
200-
/// use datafusion_execution::async_stream;
201-
/// use futures::StreamExt;
202-
///
203-
/// # #[tokio::main(flavor = "current_thread")]
204-
/// # async fn main() {
205-
/// let stream = async_stream(|mut emitter| async move {
206-
/// for i in 0_i32..3 {
207-
/// emitter.emit(i).await;
208-
/// }
209-
/// });
210-
///
211-
/// let values: Vec<i32> = stream.collect().await;
212-
/// assert_eq!(values, vec![0, 1, 2]);
213-
/// # }
214-
/// ```
215-
pub fn async_stream<T, F: Future<Output = ()>>(
216-
generator: impl FnOnce(Emitter<T>) -> F,
217-
) -> impl FusedStream<Item = T> {
218-
let (emitter, receiver) = tx_rx();
219-
AsyncStream::new(receiver, generator(emitter))
220-
}
221-
222-
#[expect(
223-
clippy::type_complexity,
224-
reason = "three-element tuple is clearer than an alias here"
225-
)]
226-
fn try_tx_rx<T, E>() -> (
227-
TryEmitter<T, E>,
228-
Emitter<Result<T, E>>,
229-
Receiver<Result<T, E>>,
230-
) {
231-
let slot = Arc::new(Mutex::new(None));
232-
(
233-
TryEmitter {
234-
slot: Arc::clone(&slot),
235-
},
236-
Emitter {
237-
slot: Arc::clone(&slot),
238-
},
239-
Receiver { slot },
240-
)
241-
}
242-
243-
/// Creates a fallible [`Stream`] from an async generator function.
244-
///
245-
/// The `generator` closure receives a [`TryEmitter<T, E>`] and runs as an
246-
/// async block that returns `Result<(), E>`. Each `emitter.emit(value).await`
247-
/// call suspends the generator and produces `Ok(value)` as the next stream
248-
/// item. The `?` operator can be used inside the generator to short-circuit on
249-
/// errors: the error is emitted as the final `Err(e)` item and the stream
250-
/// ends. The stream also ends when the generator future resolves to `Ok(())`.
251-
///
252-
/// # Example
253-
///
254-
/// ```
255-
/// use datafusion_execution::async_try_stream;
256-
/// use futures::StreamExt;
257-
///
258-
/// # #[tokio::main(flavor = "current_thread")]
259-
/// # async fn main() {
260-
/// let stream = async_try_stream(|mut emitter| async move {
261-
/// emitter.emit(1_i32).await;
262-
/// emitter.emit(2_i32).await;
263-
/// Err::<(), _>("something went wrong")?;
264-
/// emitter.emit(3_i32).await; // never reached
265-
/// Ok(())
266-
/// });
267-
///
268-
/// let values: Vec<Result<i32, &str>> = stream.collect().await;
269-
/// assert_eq!(values, vec![Ok(1), Ok(2), Err("something went wrong")]);
270-
/// # }
271-
/// ```
272-
pub fn async_try_stream<T, E, F: Future<Output = Result<(), E>>>(
273-
generator: impl FnOnce(TryEmitter<T, E>) -> F,
274-
) -> impl FusedStream<Item = Result<T, E>> {
275-
let (try_emitter, mut emitter, receiver) = try_tx_rx::<T, E>();
276-
AsyncStream::new(receiver, async move {
277-
if let Err(e) = generator(try_emitter).await {
278-
emitter.emit(Err(e)).await
279-
}
280-
})
281-
}
282-
283287
#[cfg(test)]
284288
mod test {
285289
use crate::async_stream::Emitter;

0 commit comments

Comments
 (0)