Execution Outcomes

Standout carries shell semantics as typed data from argument parsing through dispatch, rendering, hooks, output files, and final writes. Applications can let App::run own output without losing the distinction between a usage error and a runtime failure.

Status and streams

OutcomeStream used by run()Status
Help or versionstdout0
Successful rendered text or binarystdout0
Output::Silentnone0
--output-file-path successfile only0
Artifact written to a filebytes to the file, report to stdout0
Artifact written to stdoutbytes to stdout, report to stderr0
Artifact with no selectable destinationstderr1
Clap usage errorstderr2
Handler, hook, render, pipe, or write failurestderr1
Application-declared external failurestderrexact declared nonzero status

Warning flushing happens after the primary output and does not replace its status. A final text or binary write failure does replace a successful status with 1.

Capturing typed metadata

run_to_string keeps output in-process and returns RunResult. Existing string-oriented accessors remain available, while typed methods expose the semantics:

#![allow(unused)]
fn main() {
use standout::cli::{ExitStatus, RunResult, SuccessKind};

let result = app.run_to_string(command, args);

match &result {
    RunResult::Handled(output) => println!("{}", output),
    RunResult::Binary(bytes, filename) => consume(bytes, filename),
    RunResult::Artifact(run) => {
        // The framework already wrote a file destination; the receipt says where.
        println!("{:?}: {:?}", run.destination(), run.report());
    }
    RunResult::Error(error) => eprintln!("{}", error),
    RunResult::NoMatch(matches) => legacy_dispatch(matches),
    RunResult::Silent => {}
    _ => {}
}

assert_eq!(result.exit_status(), Some(ExitStatus::SUCCESS));
assert_eq!(result.success_kind(), Some(SuccessKind::Command));
assert_eq!(result.error_kind(), None);
}

RunOutput and RunError dereference to str, implement Display, and expose as_str() / into_string() for callers that used the tuple payloads as text. RunError::kind() identifies ClapUsage, Handler, Hook(phase), Render, or FinalWrite(Text|Binary|Artifact). External identifies the narrow application-declared external path; its exit_status() is the exact declared nonzero status and its text is the verbatim diagnostic payload.

No-match is a handoff, not an error

RunResult::NoMatch retains the parsed ArgMatches for partial adoption. It has no framework exit status: exit_status() returns None, run() returns false, and Standout emits nothing. The fallback dispatcher still owns that command and its eventual status.

Framework-owned final writes

run() writes successful text and binary bytes to stdout, diagnostics to stderr, and exits with the typed non-zero status when execution fails. The suggested filename on binary output remains available to capture callers; use --output-file-path when the framework should write either text or binary to a file instead of stdout.

Capture APIs do not perform the final stdout/stderr write, but file redirection is part of dispatch and therefore reports typed FinalWrite failures directly. External failures are never redirected to an output file: they remain stderr diagnostics when run() performs the final write.

Compound artifacts

Output::Artifact extends the framework-owned write to commands that also have something to say about it. The application returns bytes, an optional suggested destination, and an optional report; Standout selects the destination, writes, and only then renders the report with a receipt naming where the bytes landed. Ordering is the guarantee: a failed write produces FinalWrite(Artifact) and no report at all, so a success message can never promise a file that never appeared.

See Handler Contract for the destination policy, the report envelope, and the artifact-to-stdout report channel.