Skip to main content

kawauso_process/run/
mod.rs

1//! The run of a command that the caller observes while it runs
2//!
3//! A run in one call reports after the command ended. An application that
4//! shows the progress of a command, or that turns the output of a command
5//! into events of its own, needs the output earlier. This module holds the
6//! handle that gives the output to such a caller, one line at a time, and the
7//! types of a line.
8//!
9//! The handle reports the same result as a run in one call, so an application
10//! that moves from the one form to the other keeps its handling of the
11//! result.
12
13pub mod line;
14pub mod stream;
15pub mod text;
16
17use std::collections::VecDeque;
18use std::future::Future;
19use std::future::poll_fn;
20use std::io::Error as IoError;
21#[cfg(unix)]
22use std::os::fd::AsFd;
23use std::pin::Pin;
24use std::pin::pin;
25use std::process::ExitStatus;
26use std::task::Context;
27use std::task::Poll;
28use std::time::Duration;
29use std::time::Instant;
30
31#[cfg(unix)]
32use rustix::io::Errno;
33#[cfg(unix)]
34use rustix::io::read;
35#[cfg(unix)]
36use rustix::process::Pid;
37#[cfg(unix)]
38use rustix::process::Signal;
39#[cfg(unix)]
40use rustix::process::kill_process;
41use tokio::io::AsyncRead;
42use tokio::io::ReadBuf;
43use tokio::process::Child;
44use tokio::process::ChildStderr;
45use tokio::process::ChildStdout;
46use tokio::time::timeout;
47
48pub use self::line::Line;
49pub use self::stream::Stream;
50pub use self::text::Text;
51use crate::error::RunCommandError;
52use crate::execution::Capture;
53use crate::execution::Execution;
54use crate::invocation::Invocation;
55use crate::process_id::ProcessId;
56
57/// The number of bytes that one read takes from a stream
58///
59/// The value is a compromise. A small buffer reads a stream in many steps,
60/// and a large one holds memory that most commands never fill, because a
61/// command writes lines and not blocks.
62const CHUNK: usize = 8 * 1024;
63
64/// The number of bytes that one take reads from a stream at most
65///
66/// A pipe of the operating system holds far less than this, so a command that
67/// ended left less than this in its stream. Bytes beyond this number come
68/// from a program that still writes. A take that follows such a program does
69/// not end.
70#[cfg(unix)]
71const LEFTOVER: usize = 4 * 1024 * 1024;
72
73/// The run of a command that the caller observes while it runs
74///
75/// The handle starts with the command and ends with the result of the run. It
76/// gives the output of the command to the caller as lines, in the order in
77/// which the lines arrive, and it collects the same capture that a run in one
78/// call collects. A caller that reads no line at all therefore still gets
79/// every byte that the command wrote.
80///
81/// The handle owns the command. A caller that drops the handle ends the
82/// command with it, so a timeout, a `select`, and a scheduler that abandons a
83/// run leave no process behind. The kill that ends such a command is
84/// immediate, and a caller that wants the command to end in good order calls
85/// [`stop`][stop] instead. A caller that waits for the end of the command in a
86/// `select` keeps the handle with [`wait_for_end`][wait-for-end].
87///
88/// # Examples
89///
90/// ```no_run
91/// use kawauso_process::Invocation;
92///
93/// # #[tokio::main(flavor = "current_thread")]
94/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
95/// let mut run = Invocation::new("cargo").arg("build").start()?;
96///
97/// while let Some(line) = run.next_line().await? {
98///     println!("[{}] {line}", line.stream());
99/// }
100///
101/// let execution = run.wait().await?.require_success()?;
102///
103/// println!("{}", execution.duration().as_secs());
104/// # Ok(())
105/// # }
106/// ```
107///
108/// [stop]: Run::stop
109/// [wait-for-end]: Run::wait_for_end
110#[derive(Debug)]
111pub struct Run {
112    /// The command that runs
113    ///
114    /// The handle carries it so that a log line and an error message can name
115    /// the command without the caller holding it a second time.
116    invocation: Invocation,
117
118    /// The command itself
119    child: Child,
120
121    /// The identifier that the operating system gave the command
122    ///
123    /// The handle records the value when the command starts. The crate can no
124    /// longer read the identifier after it collected the status of the
125    /// command. A caller can collect that status and keep the handle, so a
126    /// value that the handle read on demand is gone.
127    id: Option<ProcessId>,
128
129    /// The standard output of the command
130    stdout: Reader<ChildStdout>,
131
132    /// The standard error of the command
133    stderr: Reader<ChildStderr>,
134
135    /// The lines that the readers produced and the caller has not taken
136    ///
137    /// One read of a stream can produce more than one line, and the caller
138    /// takes one line per call. The queue holds the rest, in the order in
139    /// which the lines arrived.
140    lines: VecDeque<Line>,
141
142    /// The moment at which the command started
143    started: Instant,
144}
145
146impl Run {
147    /// Takes the streams of a command that started and builds the handle
148    ///
149    /// The constructor is not public, because it takes the command as the
150    /// asynchronous runtime represents it, and no public signature of this
151    /// crate names a type of that runtime. [`start`][start] is the way to a
152    /// handle.
153    ///
154    /// [start]: Invocation::start
155    pub(crate) fn new(invocation: Invocation, mut child: Child, started: Instant) -> Self {
156        // The command started with a pipe on each stream, so both are here.
157        // A reader without a stream reports the end of its stream at once,
158        // which leaves the caller with the streams that it can read.
159        let stdout = Reader::new(Stream::StandardOutput, child.stdout.take());
160        let stderr = Reader::new(Stream::StandardError, child.stderr.take());
161
162        // The command runs, so the operating system has given it an
163        // identifier. The handle takes the value now, because the crate can
164        // no longer read it after it collected the status of the command.
165        // process[impl run.identity]
166        let id = child.id().map(ProcessId::from);
167
168        Self {
169            invocation,
170            child,
171            id,
172            stdout,
173            stderr,
174            lines: VecDeque::new(),
175            started,
176        }
177    }
178
179    /// Reads the output of the command until both streams reached their end
180    ///
181    /// The capture grows with every read, so a drain fills it whether or not
182    /// the caller took a single line. The lines that no one took end here.
183    ///
184    /// # Errors
185    ///
186    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
187    /// cannot be read.
188    ///
189    /// [incomplete]: RunCommandError::IncompleteRun
190    async fn drain(&mut self) -> Result<(), RunCommandError> {
191        while self.next_line().await?.is_some() {}
192
193        Ok(())
194    }
195
196    /// Reads the output of the command and collects its status
197    ///
198    /// The method is the work that every wait shares. It drains both streams
199    /// first. A command that fills a pipe stops until a reader empties it, and
200    /// such a command never reaches its own end.
201    ///
202    /// The crate collects the status of a command once, and this method
203    /// reports the same status on every call that follows.
204    ///
205    /// # Errors
206    ///
207    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
208    /// cannot be read, or when the end of the command cannot be waited for.
209    ///
210    /// # Cancel safety
211    ///
212    /// The method is cancel safe. The drain is cancel safe, and the status of
213    /// the command lives in the command and not in the future of this call. A
214    /// caller that drops the future therefore loses no output and no status.
215    ///
216    /// [incomplete]: RunCommandError::IncompleteRun
217    async fn end(&mut self) -> Result<ExitStatus, RunCommandError> {
218        self.drain().await?;
219
220        self.child
221            .wait()
222            .await
223            .map_err(|source| RunCommandError::IncompleteRun {
224                invocation: Box::new(self.invocation.clone()),
225                source,
226            })
227    }
228
229    /// Waits for the exit of the command and reads both streams while it waits
230    ///
231    /// A command that fills a pipe stops until a reader empties it, so a wait
232    /// that reads nothing waits for a command that cannot end. The read
233    /// therefore runs with the wait, and the capture grows while the command
234    /// ends.
235    ///
236    /// The method returns as soon as the operating system reports the status
237    /// of the command. It does not wait for the end of the streams, which is
238    /// where it differs from [`end`][end]. A program that starts programs of
239    /// its own gives them its streams. Such a program can end while a program
240    /// that it started holds the streams open.
241    ///
242    /// # Errors
243    ///
244    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
245    /// cannot be read, or when the end of the command cannot be waited for.
246    ///
247    /// [end]: Run::end
248    /// [incomplete]: RunCommandError::IncompleteRun
249    async fn exit(&mut self) -> Result<ExitStatus, RunCommandError> {
250        let Self {
251            invocation,
252            child,
253            stdout,
254            stderr,
255            lines,
256            ..
257        } = self;
258
259        // The wait holds its state in the future, so the method builds the
260        // future once and polls it again in every round.
261        let mut wait = pin!(child.wait());
262
263        let exited = poll_fn(|context| {
264            // The read comes first, because a command that fills a pipe ends
265            // only after something empties it.
266            let produced = match poll_streams(stdout, stderr, lines, context) {
267                Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
268                Poll::Ready(Ok(())) => true,
269                Poll::Pending => false,
270            };
271
272            if let Poll::Ready(exited) = wait.as_mut().poll(context) {
273                return Poll::Ready(exited);
274            }
275
276            // A stream that gave bytes registered no interest with the
277            // runtime, because it has nothing to wait for. The runtime
278            // therefore wakes nothing, and the call wakes itself instead.
279            if produced {
280                context.waker().wake_by_ref();
281            }
282
283            Poll::Pending
284        })
285        .await;
286
287        exited.map_err(|source| RunCommandError::IncompleteRun {
288            invocation: Box::new(invocation.clone()),
289            source,
290        })
291    }
292
293    /// Builds the result of a run that ended
294    ///
295    /// Every wait and the stop report the same result, and the method holds
296    /// the one place that builds it. The status arrives as an argument,
297    /// because the handle does not keep it.
298    fn finish(self, status: ExitStatus) -> Execution {
299        Execution::new(
300            self.invocation,
301            self.id,
302            status,
303            Capture::new(self.stdout.capture),
304            Capture::new(self.stderr.capture),
305            self.started.elapsed(),
306        )
307    }
308
309    /// Returns the identifier that the operating system gave the command
310    ///
311    /// An application that reports which program it started, or that gives
312    /// the command to a tool of the platform, reads the identifier here. The
313    /// handle records the value when the command starts, so the value is
314    /// there for the whole life of the handle. A wait for the end of the
315    /// command does not remove the value.
316    ///
317    /// The operating system takes the identifier of a command that ended, and
318    /// it can give the same identifier to another command. A caller that
319    /// waited for the end therefore reads the identifier of the command that
320    /// ran, and not the identifier of a command that runs.
321    ///
322    /// The value is `None` when the operating system gave the command no
323    /// identifier.
324    // process[impl stream.identity]
325    pub fn id(&self) -> Option<ProcessId> {
326        self.id
327    }
328
329    /// Returns the command that runs
330    pub fn invocation(&self) -> &Invocation {
331        &self.invocation
332    }
333
334    /// Kills the command without a request to end
335    ///
336    /// A kill that the operating system refuses names a process that it no
337    /// longer knows, which is a command that ended. The wait that follows
338    /// reports how the command ended, so the refusal tells the caller
339    /// nothing that the result does not carry.
340    fn kill(&mut self) {
341        let _ = self.child.start_kill();
342    }
343
344    /// Returns the next line of the output of the command
345    ///
346    /// The method waits until the command writes a line to one of its
347    /// streams, and it returns the lines in the order in which they arrive.
348    /// It returns `None` when the command closed both of its streams, which a
349    /// command does when it ends. A command that writes a last line without
350    /// the characters that end a line still reports that line.
351    ///
352    /// Every line that the method returns stays in the capture of the result
353    /// as well, so a caller that shows the output while the command runs and
354    /// then reports the whole output reads both from the same run.
355    ///
356    /// # Errors
357    ///
358    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
359    /// cannot be read.
360    ///
361    /// # Cancel safety
362    ///
363    /// The method is cancel safe. A caller often waits for a line and for
364    /// another event, such as a timeout or another branch of a `select`. The
365    /// caller drops the future of the call when the other event occurs first,
366    /// and the call loses no output. The bytes that the call took stay with
367    /// the handle, and the call that follows reports the line that they
368    /// belong to.
369    ///
370    /// # Examples
371    ///
372    /// ```no_run
373    /// use kawauso_process::Invocation;
374    ///
375    /// # #[tokio::main(flavor = "current_thread")]
376    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
377    /// let mut run = Invocation::new("cargo").arg("test").start()?;
378    ///
379    /// while let Some(line) = run.next_line().await? {
380    ///     println!("{line}");
381    /// }
382    /// # Ok(())
383    /// # }
384    /// ```
385    ///
386    /// [incomplete]: RunCommandError::IncompleteRun
387    // process[impl stream]
388    // process[impl stream.cancellation]
389    pub async fn next_line(&mut self) -> Result<Option<Line>, RunCommandError> {
390        loop {
391            if let Some(line) = self.lines.pop_front() {
392                return Ok(Some(line));
393            }
394
395            if self.stdout.is_closed() && self.stderr.is_closed() {
396                return Ok(None);
397            }
398
399            // A poll that takes bytes stores them in the reader and in the
400            // queue before it returns. The await point below is a poll that
401            // took nothing. Every state of the read therefore lives in the
402            // handle, and a future that the caller drops takes none of it.
403            let read = {
404                let Self {
405                    stdout,
406                    stderr,
407                    lines,
408                    ..
409                } = self;
410
411                poll_fn(|context| poll_streams(stdout, stderr, lines, context)).await
412            };
413
414            read.map_err(|source| RunCommandError::IncompleteRun {
415                invocation: Box::new(self.invocation.clone()),
416                source,
417            })?;
418        }
419    }
420
421    /// Asks the command to end, and reports whether the request went out
422    ///
423    /// A Unix platform has `SIGTERM`, which is the request that a program
424    /// answers when it ends its work in good order. The request reaches the
425    /// process of the command alone, and not a process group, so a command
426    /// that starts programs of its own decides what happens to them.
427    ///
428    /// A process that the operating system no longer knows, and a request
429    /// that it refuses, both report that nothing asked the command to end.
430    /// The stop then kills, because no answer can arrive.
431    ///
432    /// The method asks the operating system for the identifier of the
433    /// command, and it does not read the identifier that the handle reports.
434    /// The handle reports a recorded value, and the operating system can give
435    /// that identifier to another command after the command of this handle
436    /// ended. A request that reached that other command ends a program of
437    /// somebody else.
438    #[cfg(unix)]
439    fn request_end(&self) -> Request {
440        let Some(identifier) = self.child.id() else {
441            return Request::Unsent;
442        };
443
444        let Ok(identifier) = i32::try_from(identifier) else {
445            return Request::Unsent;
446        };
447
448        let Some(process) = Pid::from_raw(identifier) else {
449            return Request::Unsent;
450        };
451
452        match kill_process(process, Signal::TERM) {
453            Ok(()) => Request::Sent,
454            Err(_) => Request::Unsent,
455        }
456    }
457
458    /// Reports that this platform has no request to end
459    ///
460    /// Windows has no signal that asks a program to end its work, and the
461    /// crate has no interface of the operating system that takes its place.
462    /// The stop therefore kills the command at once, which the
463    /// documentation of the stop states.
464    #[cfg(not(unix))]
465    fn request_end(&self) -> Request {
466        Request::Unsent
467    }
468
469    /// Ends the command and reports the result of the run
470    ///
471    /// The method asks the command to end, and it gives the command the time
472    /// that the caller names. It kills a command that is still there when
473    /// the time is over. A program that answers the request removes its lock
474    /// file, writes what it has, and ends, which a kill gives it no time to
475    /// do.
476    ///
477    /// The request is `SIGTERM` on a Unix platform. A platform that has no
478    /// request of this kind kills the command at once, and the time then
479    /// passes unused. The request reaches the program that the crate
480    /// started, and not a group of processes, so a program that starts
481    /// programs of its own decides what happens to them.
482    ///
483    /// The time measures the command, and not the streams of the command. A
484    /// program that starts programs of its own gives them its streams. Such a
485    /// program can end while a program that it started holds the streams
486    /// open.
487    ///
488    /// The method reads both streams while it waits, so a command that fills
489    /// a pipe ends as well. It takes what the streams hold when the command
490    /// ends. On a Unix platform, a program that holds a stream open therefore
491    /// does not hold the call. On a platform that has no read of this kind,
492    /// the call waits for the end of both streams.
493    ///
494    /// The method reports what [`wait`][wait] reports: the exit status, the
495    /// capture of both streams, and the time that the run took.
496    ///
497    /// A caller that drops the handle instead of this call kills the command
498    /// at once, with no request before it.
499    ///
500    /// # Errors
501    ///
502    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
503    /// cannot be read, or when the end of the command cannot be waited for.
504    ///
505    /// # Panics
506    ///
507    /// Panics when the Tokio runtime that drives the future has no timer.
508    /// The method waits for the time that the caller names, and the timer of
509    /// the runtime measures it.
510    ///
511    /// # Examples
512    ///
513    /// ```no_run
514    /// use std::time::Duration;
515    ///
516    /// use kawauso_process::Invocation;
517    ///
518    /// # #[tokio::main(flavor = "current_thread")]
519    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
520    /// let run = Invocation::new("cargo").arg("build").start()?;
521    ///
522    /// let execution = run.stop(Duration::from_secs(5)).await?;
523    ///
524    /// println!("{}", execution.status());
525    /// # Ok(())
526    /// # }
527    /// ```
528    ///
529    /// [incomplete]: RunCommandError::IncompleteRun
530    /// [wait]: Run::wait
531    // process[impl stop]
532    // process[impl stop.request]
533    pub async fn stop(mut self, grace: Duration) -> Result<Execution, RunCommandError> {
534        let exited = match self.request_end() {
535            // The command has the request, so it has the time to answer it.
536            // The wait reads what the command writes while it answers, so a
537            // command that fills a pipe reaches its own end.
538            // process[impl stop.grace]
539            // process[impl stop.bound]
540            Request::Sent => match timeout(grace, self.exit()).await {
541                Ok(exited) => Some(exited?),
542                Err(_) => None,
543            },
544
545            // Nothing asked the command to end, so nothing waits for an
546            // answer. The kill below is what this platform has.
547            Request::Unsent => None,
548        };
549
550        let status = match exited {
551            Some(status) => status,
552
553            // The command ignored the request, or nothing asked it to end.
554            // The kill ends it, and the wait that follows collects the status
555            // of a command that can no longer refuse.
556            // process[impl stop.kill]
557            None => {
558                self.kill();
559
560                self.exit().await?
561            }
562        };
563
564        // The command ended, and what it wrote last can still be in a pipe.
565        // process[impl stop.bound]
566        self.take().await?;
567
568        Ok(self.finish(status))
569    }
570
571    /// Reads what the streams of the command hold, and waits for nothing
572    ///
573    /// A command that ended wrote what it wrote, and the last of it can still
574    /// be in the pipe that carries a stream. The method takes those bytes, so
575    /// that the capture of the result holds them.
576    ///
577    /// The method waits for no stream. A program that the command started
578    /// inherits the streams of the command, and it holds them open for as
579    /// long as it runs. A read that waits for the end of such a stream waits
580    /// for a program that nothing asked to end.
581    ///
582    /// # Errors
583    ///
584    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
585    /// cannot be read.
586    ///
587    /// [incomplete]: RunCommandError::IncompleteRun
588    #[cfg(unix)]
589    async fn take(&mut self) -> Result<(), RunCommandError> {
590        let Self {
591            invocation,
592            stdout,
593            stderr,
594            lines,
595            ..
596        } = self;
597
598        let taken = stdout.take(lines).and_then(|()| stderr.take(lines));
599
600        taken.map_err(|source| RunCommandError::IncompleteRun {
601            invocation: Box::new(invocation.clone()),
602            source,
603        })
604    }
605
606    /// Reads both streams of the command until they reached their end
607    ///
608    /// This platform has no read that reports what a stream holds without a
609    /// wait, so the method waits for the end of both streams. A program that
610    /// the command started inherits the streams, and it holds the call for as
611    /// long as it runs.
612    ///
613    /// # Errors
614    ///
615    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
616    /// cannot be read.
617    ///
618    /// [incomplete]: RunCommandError::IncompleteRun
619    #[cfg(not(unix))]
620    async fn take(&mut self) -> Result<(), RunCommandError> {
621        self.drain().await
622    }
623
624    /// Waits for the end of the command and reports the result of the run
625    ///
626    /// The method reads what the command still writes, waits for the end of
627    /// the command, and reports what a run in one call reports: the exit
628    /// status, the capture of both streams, and the time that the run took.
629    /// The capture holds everything that the command wrote, whether or not
630    /// the caller read a line.
631    ///
632    /// A command that ends without success is no failure of this method. The
633    /// status travels in the result, and
634    /// [`require_success`][require-success] turns a status that is not a
635    /// success into an error.
636    ///
637    /// # Errors
638    ///
639    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
640    /// cannot be read, or when the end of the command cannot be waited for.
641    ///
642    /// # Cancel safety
643    ///
644    /// The method is not cancel safe. It takes the handle, so a caller that
645    /// drops the future of a call ends the command. The caller loses the
646    /// result of the run, and the capture of both streams with it. A caller
647    /// that waits for the end of the command, and for an event of its own,
648    /// calls [`wait_for_end`][wait-for-end]. That method leaves the handle.
649    ///
650    /// # Examples
651    ///
652    /// ```no_run
653    /// use kawauso_process::Invocation;
654    ///
655    /// # #[tokio::main(flavor = "current_thread")]
656    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
657    /// let run = Invocation::new("cargo").arg("build").start()?;
658    ///
659    /// let execution = run.wait().await?;
660    ///
661    /// println!("{}", execution.status());
662    /// # Ok(())
663    /// # }
664    /// ```
665    ///
666    /// [incomplete]: RunCommandError::IncompleteRun
667    /// [require-success]: Execution::require_success
668    /// [wait-for-end]: Run::wait_for_end
669    // process[impl stream.wait]
670    pub async fn wait(mut self) -> Result<Execution, RunCommandError> {
671        let status = self.end().await?;
672
673        Ok(self.finish(status))
674    }
675
676    /// Waits for the end of the command and leaves the handle with the caller
677    ///
678    /// A caller often waits for the end of a command and for an event of its
679    /// own, such as a token that cancels the work. [`wait`][wait] takes the
680    /// handle, and this caller needs it. The caller holds nothing when the
681    /// event comes first, and [`stop`][stop] needs the handle. This method
682    /// reports the end of the command, and it leaves the handle. The caller
683    /// then asks for the result, or it stops a command that still runs.
684    ///
685    /// The method reports no result of its own. The handle keeps everything
686    /// that a result needs, so [`wait`][wait] reports the result after this
687    /// call. The wait that follows returns at once, because the command
688    /// ended.
689    ///
690    /// The method reads both streams while it waits, so a command that fills
691    /// a pipe ends as well. It waits for the command and not for the output
692    /// of the command. A command that closed both of its streams can still
693    /// run, and this method reports that end.
694    ///
695    /// # Errors
696    ///
697    /// Returns [`IncompleteRun`][incomplete] when a stream of the command
698    /// cannot be read, or when the end of the command cannot be waited for.
699    ///
700    /// # Cancel safety
701    ///
702    /// The method is cancel safe. The caller drops the future of the call
703    /// when the other event occurs first, and the command continues. The
704    /// handle keeps the output that the call read, and the caller stops the
705    /// command, takes the result, or waits again.
706    ///
707    /// # Examples
708    ///
709    /// ```no_run
710    /// use std::time::Duration;
711    ///
712    /// use kawauso_process::Invocation;
713    ///
714    /// # async fn cancelled() {}
715    /// # #[tokio::main(flavor = "current_thread")]
716    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
717    /// let mut run = Invocation::new("cargo").arg("build").start()?;
718    ///
719    /// let execution = tokio::select! {
720    ///     ended = run.wait_for_end() => {
721    ///         ended?;
722    ///         run.wait().await?
723    ///     }
724    ///     () = cancelled() => run.stop(Duration::from_secs(5)).await?,
725    /// };
726    ///
727    /// println!("{}", execution.status());
728    /// # Ok(())
729    /// # }
730    /// ```
731    ///
732    /// [incomplete]: RunCommandError::IncompleteRun
733    /// [stop]: Run::stop
734    /// [wait]: Run::wait
735    // process[impl stream.end]
736    // process[impl stream.end.cancellation]
737    pub async fn wait_for_end(&mut self) -> Result<(), RunCommandError> {
738        self.end().await?;
739
740        Ok(())
741    }
742}
743
744/// Whether the request to end reached the command
745///
746/// A platform without a request of this kind, and a process that the
747/// operating system no longer knows, both leave the kill as the only way to
748/// end a run. A stop that asked nothing must not wait for an answer, because
749/// no answer can arrive.
750#[derive(Debug)]
751enum Request {
752    /// The command received the request to end
753    Sent,
754
755    /// Nothing asked the command to end
756    Unsent,
757}
758
759/// Reads the streams of a command until one of them produced something
760///
761/// The function polls both streams on every call, so a command that fills the
762/// pipe of one stream while it is quiet on the other never stops the run. A
763/// stream that produced nothing has registered its interest with the runtime,
764/// which wakes the caller when the stream has bytes.
765///
766/// The function returns `Pending` when both streams have nothing. A stream
767/// that reached its end reports nothing forever, so the caller stops before
768/// it calls this function with two such streams.
769///
770/// # Errors
771///
772/// Returns the error of the stream that could not be read.
773fn poll_streams(
774    stdout: &mut Reader<ChildStdout>,
775    stderr: &mut Reader<ChildStderr>,
776    lines: &mut VecDeque<Line>,
777    context: &mut Context<'_>,
778) -> Poll<Result<(), IoError>> {
779    let mut produced = false;
780
781    match stdout.poll(context, lines) {
782        Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
783        Poll::Ready(Ok(())) => produced = true,
784        Poll::Pending => {}
785    }
786
787    match stderr.poll(context, lines) {
788        Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
789        Poll::Ready(Ok(())) => produced = true,
790        Poll::Pending => {}
791    }
792
793    if produced {
794        Poll::Ready(Ok(()))
795    } else {
796        Poll::Pending
797    }
798}
799
800/// One stream of a command, with the capture and the line that it builds
801///
802/// The reader keeps three things apart. The capture holds every byte that the
803/// stream carried, because the result of the run reports the output as the
804/// command wrote it. The pending bytes hold the line that the command has
805/// started and not ended. The stream tells a line where it came from.
806#[derive(Debug)]
807struct Reader<S> {
808    /// The stream that this reader reads
809    stream: Stream,
810
811    /// The stream itself, and `None` after the stream reached its end
812    source: Option<S>,
813
814    /// The bytes of a line that the command has not ended
815    pending: Vec<u8>,
816
817    /// The number of pending bytes in which no end of a line was found
818    ///
819    /// A read looks for the end of a line in the bytes that it added, and not
820    /// in the bytes that it has looked at before. A command that writes a
821    /// long line therefore reads that line once, and not once per read.
822    searched: usize,
823
824    /// Every byte that the stream carried
825    capture: Vec<u8>,
826}
827
828impl<S: AsyncRead + Unpin> Reader<S> {
829    /// Creates the reader of one stream of a command
830    fn new(stream: Stream, source: Option<S>) -> Self {
831        Self {
832            stream,
833            source,
834            pending: Vec::new(),
835            searched: 0,
836            capture: Vec::new(),
837        }
838    }
839
840    /// Keeps the bytes of one read and builds the lines that they completed
841    fn accept(&mut self, read: &[u8], lines: &mut VecDeque<Line>) {
842        self.capture.extend_from_slice(read);
843        self.pending.extend_from_slice(read);
844        self.split(lines);
845    }
846
847    /// Marks the end of the stream and reports a line that has no end
848    ///
849    /// A command that wrote a last line without the characters that end a
850    /// line still wrote that line, because nothing follows it.
851    fn close(&mut self, lines: &mut VecDeque<Line>) {
852        self.source = None;
853
854        if !self.pending.is_empty() {
855            let bytes = std::mem::take(&mut self.pending);
856
857            self.searched = 0;
858            lines.push_back(self.line(&bytes));
859        }
860    }
861
862    /// Returns whether the stream reached its end
863    fn is_closed(&self) -> bool {
864        self.source.is_none()
865    }
866
867    /// Builds one line from the bytes that the command wrote
868    ///
869    /// A byte that is part of no valid character becomes the replacement
870    /// character, because a line is for a display and a display takes text.
871    /// The capture keeps the byte, so nothing that the command wrote is lost.
872    // process[impl stream.decode]
873    fn line(&self, bytes: &[u8]) -> Line {
874        Line::new(self.stream, String::from_utf8_lossy(bytes).into_owned())
875    }
876
877    /// Reads what the stream has and builds the lines that it completed
878    ///
879    /// The function returns `Ready` when it read bytes or when the stream
880    /// reached its end, and `Pending` when the stream has nothing yet. A
881    /// stream that reached its end reports `Pending`, because it has nothing
882    /// to report ever again.
883    ///
884    /// # Errors
885    ///
886    /// Returns the error of a stream that cannot be read.
887    fn poll(
888        &mut self,
889        context: &mut Context<'_>,
890        lines: &mut VecDeque<Line>,
891    ) -> Poll<Result<(), IoError>> {
892        let Some(source) = self.source.as_mut() else {
893            return Poll::Pending;
894        };
895
896        let mut bytes = [0; CHUNK];
897        let mut buffer = ReadBuf::new(&mut bytes);
898
899        match Pin::new(source).poll_read(context, &mut buffer) {
900            Poll::Pending => return Poll::Pending,
901            Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
902            Poll::Ready(Ok(())) => {}
903        }
904
905        let read = buffer.filled();
906
907        // A read of no bytes is the end of the stream.
908        if read.is_empty() {
909            self.close(lines);
910
911            return Poll::Ready(Ok(()));
912        }
913
914        self.accept(read, lines);
915
916        Poll::Ready(Ok(()))
917    }
918
919    /// Reads what the stream holds, and waits for nothing
920    ///
921    /// A read of a stream that holds no bytes reports that it found nothing,
922    /// and that ends the loop. The stream can be open at that moment: a
923    /// program that the command started inherited the stream, and it wrote
924    /// nothing. A program that writes without an end cannot hold the loop
925    /// either, because the loop reads no more than [`LEFTOVER`] bytes.
926    ///
927    /// The read goes to the stream itself, and not through the runtime. The
928    /// runtime reports a stream as quiet until it looked at the stream again,
929    /// and this call gives it no time to look.
930    ///
931    /// # Errors
932    ///
933    /// Returns the error of a stream that cannot be read.
934    #[cfg(unix)]
935    fn take(&mut self, lines: &mut VecDeque<Line>) -> Result<(), IoError>
936    where
937        S: AsFd,
938    {
939        let mut bytes = [0; CHUNK];
940        let mut taken = 0;
941
942        while taken < LEFTOVER {
943            let Some(source) = self.source.as_ref() else {
944                return Ok(());
945            };
946
947            match read(source, &mut bytes) {
948                Ok(0) => self.close(lines),
949                Ok(count) => {
950                    taken += count;
951                    self.accept(&bytes[..count], lines);
952                }
953
954                // A signal that arrived during the read took nothing from
955                // the stream, so the read starts again.
956                Err(Errno::INTR) => {}
957
958                // The stream is open and holds nothing, which is what ends
959                // this call.
960                Err(error) if error == Errno::AGAIN || error == Errno::WOULDBLOCK => {
961                    return Ok(());
962                }
963
964                Err(error) => return Err(error.into()),
965            }
966        }
967
968        Ok(())
969    }
970
971    /// Takes every line that the command ended out of the pending bytes
972    ///
973    /// A command on Windows ends a line with two characters, and a command on
974    /// every other platform with one. The line carries neither of them, so a
975    /// caller that shows the line, or that compares it, reads the same text
976    /// on every platform.
977    // process[impl stream.crlf]
978    fn split(&mut self, lines: &mut VecDeque<Line>) {
979        while let Some(offset) = self.pending[self.searched..]
980            .iter()
981            .position(|byte| *byte == b'\n')
982        {
983            let end = self.searched + offset;
984            let mut bytes: Vec<u8> = self.pending.drain(..=end).collect();
985
986            bytes.pop();
987
988            if bytes.last() == Some(&b'\r') {
989                bytes.pop();
990            }
991
992            self.searched = 0;
993            lines.push_back(self.line(&bytes));
994        }
995
996        self.searched = self.pending.len();
997    }
998}
999
1000#[cfg(test)]
1001mod tests {
1002    // An assertion in a test panics by design. A `# Panics` section on every
1003    // test would repeat that and give the reader no information.
1004    #![allow(clippy::missing_panics_doc)]
1005
1006    use super::*;
1007
1008    // A caller keeps the handle in a task that another thread drives. This
1009    // test holds the type to the auto traits that make this possible, because
1010    // a private field of a later version could take them away without a word
1011    // from the compiler.
1012    #[test]
1013    fn run_is_send_and_sync() {
1014        fn assert_send_and_sync<T: Send + Sync>() {}
1015
1016        assert_send_and_sync::<Run>();
1017    }
1018}