pub fn output_stream<'a, R, I>(
commands: I,
concurrency: usize,
runner: &'a R,
) -> impl Stream<Item = (usize, Result<ProcessResult<String>>)> + Send + 'aExpand description
Run every command in commands with at most concurrency live at once, yielding
each result — an (input index, Result<ProcessResult<String>>) pair —
the moment that command finishes. This is the streaming sibling of
output_all: the same bounded fan-out and the same per-command error semantics
(an Err is a spawn/I/O failure; a non-zero exit is an Ok(ProcessResult); the
fan-out never short-circuits), but presented as a Stream over completions
instead of a single Vec at the end.
Key differences from output_all:
- Completion order, not input order. Items arrive as commands finish, so a
fast command never blocks behind a slow one. Each item carries the command’s
input index (its position in
commands), so you can still map a result back to its source; if you need the input-orderVec, useoutput_all(which is this stream collected by index). - Partial results survive cancellation. Every result already yielded is owned
by the consumer, so dropping the stream mid-fan-out keeps them — unlike
output_all, whoseVecmaterializes only at the very end (its “no partial results” limitation).
concurrency is clamped to at least 1. An empty commands yields an empty stream
(immediately None).
Cancellation / teardown. Dropping the stream drops the in-flight command
futures — with an own-group runner (JobRunner) that kills
every still-live process tree (no orphans), matching output_all; with a
shared-group runner (&ProcessGroup) they live until the group is torn down.
Commands still waiting for a concurrency slot are dropped without ever being
spawned — a queued command runs nothing until it is scheduled, so cancelling the
fan-out cancels them for free.
The returned stream borrows runner for 'a; consume it with
StreamExt (while let Some((i, res)) = stream.next().await).