cli_stream/error.rs
1//! Typed errors for the streaming engine.
2
3/// Why [`Command::stream`](crate::Command::stream) or
4/// [`ProcessHandle::cancel`](crate::ProcessHandle::cancel) failed.
5///
6/// Carries the real underlying [`std::io::Error`] as a source (via
7/// [`std::error::Error::source`]) rather than a pre-formatted string, so a
8/// caller can downcast or inspect the OS error (e.g. distinguish
9/// `NotFound` — the binary isn't on `PATH` — from `PermissionDenied`).
10/// `#[non_exhaustive]` so adding a variant later isn't a breaking change.
11#[derive(Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum StreamError {
14 /// The child process could not be spawned: the binary isn't on `PATH`,
15 /// isn't executable, or the OS refused. `source` is the spawn `io::Error`
16 /// (commonly `NotFound`).
17 #[error("failed to spawn {program}: {source}")]
18 Spawn {
19 /// The program that failed to launch (as passed to the engine).
20 program: String,
21 /// The OS error from `Command::spawn`.
22 #[source]
23 source: std::io::Error,
24 },
25
26 /// Writing to the child's stdin failed — most often because it has already
27 /// exited, so the pipe is gone. A caller waiting on an answer needs to hear
28 /// this rather than block forever.
29 #[error("writing to the child's stdin failed: {source}")]
30 Write {
31 /// The OS error from the write or flush.
32 #[source]
33 source: std::io::Error,
34 },
35
36 /// The spawned child didn't expose a piped stdout/stderr. Shouldn't
37 /// happen given the engine requests `Stdio::piped()`, but `Child`'s pipe
38 /// accessors return `Option`, so the case is represented rather than
39 /// `unwrap`ped.
40 #[error("child {stream} pipe was not captured")]
41 PipeNotCaptured {
42 /// Which stream was missing — `"stdin"`, `"stdout"` or `"stderr"`.
43 stream: &'static str,
44 },
45
46 /// Cancellation couldn't acquire the child lock because it was poisoned
47 /// (a thread panicked while holding it). The process may still be
48 /// running; the caller can retry or give up.
49 #[error("cancel failed: the child lock was poisoned")]
50 CancelLockPoisoned,
51}