Skip to main content

bynk/
shell.rs

1//! Shelling the resolved `bynkc` — shared by the commands that delegate to it.
2//!
3//! `bynk test` always delegates (it orchestrates external `tsc`/`node` anyway,
4//! and delegating through the driver's resolution is the #487/#486 win); `bynk
5//! check`/`fmt` delegate only under a `BYNK_BYNKC` override, so a
6//! developer-pinned compiler still governs the result. All three inherit stdio
7//! and propagate the child's exit status through [`exit_status_byte`].
8
9use std::ffi::OsStr;
10use std::path::Path;
11use std::process::{Command, ExitCode};
12
13/// Map a child's [`std::process::ExitStatus`] to a process exit byte. A normal exit
14/// propagates the code. Signal death is *not* uniformly a clean stop: a
15/// shared Ctrl-C (SIGINT) is — the terminal delivered it to us too — but a
16/// SIGSEGV or the OOM killer's SIGKILL is a real failure, and mapping it to
17/// success made a crashed \`bynkc test\` read as passing in CI. Non-SIGINT
18/// signals exit \`128 + signal\`, the shell convention.
19pub fn exit_status_byte(status: &std::process::ExitStatus) -> u8 {
20    if let Some(code) = status.code() {
21        return code.clamp(0, 255) as u8;
22    }
23    #[cfg(unix)]
24    {
25        use std::os::unix::process::ExitStatusExt;
26        if let Some(sig) = status.signal() {
27            // SIGINT is 2 on every Unix.
28            if sig == 2 {
29                return 0;
30            }
31            return 128u8.saturating_add(sig.clamp(0, 127) as u8);
32        }
33    }
34    1
35}
36
37/// Shell `bynkc <args>` at `bynkc`, inheriting stdio, and return its exit code.
38/// A spawn failure (a missing or unexecutable binary) is surfaced as a driver
39/// error naming the path, so a bad `BYNK_BYNKC` override is diagnosable.
40pub fn delegate<I, S>(bynkc: &Path, args: I) -> ExitCode
41where
42    I: IntoIterator<Item = S>,
43    S: AsRef<OsStr>,
44{
45    let mut cmd = Command::new(bynkc);
46    cmd.args(args);
47    match cmd.status() {
48        Ok(s) => ExitCode::from(exit_status_byte(&s)),
49        Err(e) => {
50            eprintln!("bynk: could not run bynkc ({}): {e}", bynkc.display());
51            ExitCode::FAILURE
52        }
53    }
54}