///|
/// Captured output and exit status from a command or pipeline.
///
/// A single command reports one element in `stage_exit_codes` and one in
/// `stage_stderr`. For a pipeline, `exit_code` uses pipefail semantics: it is
/// the rightmost non-zero stage status, or zero when every stage succeeds, and
/// `stderr` is the concatenation of `stage_stderr` in stage order.
///
/// Text fields use lossy UTF-8 decoding so arbitrary process output cannot
/// cancel a run. `stdout_bytes` and `stage_stderr_bytes` keep the exact bytes.
pub struct Output {
exit_code : Int
stage_exit_codes : Array[Int]
stdout : String
stdout_bytes : Bytes
stderr : String
stage_stderr : Array[String]
stage_stderr_bytes : Array[Bytes]
} derive(Debug)
///|
/// Whether the command, or every stage of the pipeline, exited with zero.
pub fn Output::success(self : Output) -> Bool {
self.exit_code == 0
}
///|
/// Raise `CommandFailed` when the output is not successful.
///
/// # Example
/// ```mbt check
/// #cfg(not(platform="windows"))
/// async test {
/// try @myshell.Cmd("false", []).output().check() catch {
/// @myshell.ProcessError::CommandFailed(failed) =>
/// inspect(failed.exit_code, content="1")
/// _ => fail("unexpected error")
/// } noraise {
/// _ => fail("expected CommandFailed")
/// }
/// }
/// ```
pub fn Output::check(self : Output) -> Output raise ProcessError {
if self.exit_code != 0 {
raise CommandFailed(self)
}
self
}
///|
/// Errors reported by the process EDSL itself.
///
/// These are raised before or instead of spawning; a non-zero child status is
/// reported through `Output` rather than as an error.
pub(all) suberror ProcessError {
EmptyProgram
EmptyPipeline
InvalidEnvironmentName(String)
InvalidOutputLimit(Int)
InvalidGracePeriod(Int)
OutputLimitExceeded(stream~ : String, limit~ : Int)
NulByte(String)
StdinOnNonFirstStage(Int)
RedirectOnNonFinalStage(Int)
StdoutNotCaptured
CommandFailed(Output)
} derive(Debug)
///|
pub extend Output with @debug.Debug::{to_repr}
///|
pub extend ProcessError with @debug.Debug::{to_repr}