const STDERR_MARKER: &str = "--- stderr ---\n";
pub(super) fn render(exit_code: i32, stdout: &[u8], stderr: &[u8]) -> Vec<u8> {
let mut out = format!("Exit code: {exit_code}\n").into_bytes();
out.extend_from_slice(stdout);
if !stderr.is_empty() {
if !out.ends_with(b"\n") {
out.push(b'\n');
}
out.extend_from_slice(STDERR_MARKER.as_bytes());
out.extend_from_slice(stderr);
}
out
}
#[cfg(test)]
mod tests {
use super::render;
fn rendered(exit_code: i32, stdout: &str, stderr: &str) -> String {
String::from_utf8(render(exit_code, stdout.as_bytes(), stderr.as_bytes()))
.expect("ASCII fixtures render as UTF-8")
}
#[test]
fn success_states_the_code_and_carries_stdout() {
assert_eq!(rendered(0, "hello\n", ""), "Exit code: 0\nhello\n");
}
#[test]
fn stderr_survives_a_zero_exit() {
assert_eq!(
rendered(0, "built\n", "warning: deprecated\n"),
"Exit code: 0\nbuilt\n--- stderr ---\nwarning: deprecated\n"
);
}
#[test]
fn the_exit_code_distinguishes_failures() {
assert_eq!(
rendered(127, "", "sh: nope: not found\n"),
"Exit code: 127\n--- stderr ---\nsh: nope: not found\n"
);
assert_eq!(
rendered(1, "", "assertion failed\n"),
"Exit code: 1\n--- stderr ---\nassertion failed\n"
);
}
#[test]
fn unterminated_stdout_gains_a_separator_before_the_marker() {
assert_eq!(
rendered(0, "no trailing newline", "note\n"),
"Exit code: 0\nno trailing newline\n--- stderr ---\nnote\n"
);
}
#[test]
fn an_empty_stderr_emits_no_marker() {
let out = rendered(3, "partial", "");
assert_eq!(out, "Exit code: 3\npartial");
assert!(!out.contains("stderr"));
}
#[test]
fn a_silent_command_is_not_empty_content() {
assert_eq!(rendered(0, "", ""), "Exit code: 0\n");
}
#[test]
fn non_utf8_bytes_pass_through_untouched() {
let out = render(0, &[0xff, 0xfe], &[0x80]);
assert_eq!(out, b"Exit code: 0\n\xff\xfe\n--- stderr ---\n\x80");
}
}