Skip to main content

cli_stream/
error.rs

1//! Typed errors for the streaming engine.
2
3/// Why [`spawn_streaming`](crate::spawn_streaming) 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    /// The spawned child didn't expose a piped stdout/stderr. Shouldn't
27    /// happen given the engine requests `Stdio::piped()`, but `Child`'s pipe
28    /// accessors return `Option`, so the case is represented rather than
29    /// `unwrap`ped.
30    #[error("child {stream} pipe was not captured")]
31    PipeNotCaptured {
32        /// Which stream was missing — `"stdout"` or `"stderr"`.
33        stream: &'static str,
34    },
35
36    /// Cancellation couldn't acquire the child lock because it was poisoned
37    /// (a thread panicked while holding it). The process may still be
38    /// running; the caller can retry or give up.
39    #[error("cancel failed: the child lock was poisoned")]
40    CancelLockPoisoned,
41}