vvbox 0.1.0

Lightweight sandbox runner for macOS 26 using Apple container CLI.
Documentation
//! Small utility helpers shared across the crate.

use std::io::{IsTerminal, Write};
use std::process::{Command, Stdio};

/// Print an error message to stderr and exit the process.
pub fn die(message: &str) -> ! {
    eprintln!("{}", message);
    std::process::exit(1);
}

/// Run a command and return its exit status.
///
/// When `inherit` is true, stdio is forwarded to the child process.
pub fn run_cmd(cmd: &[String], cwd: Option<&std::path::Path>, inherit: bool) -> std::process::ExitStatus {
    let mut command = Command::new(&cmd[0]);
    if cmd.len() > 1 {
        command.args(&cmd[1..]);
    }
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    if inherit {
        command.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());
    }
    match command.status() {
        Ok(status) => status,
        Err(_) => {
            die("failed to run command");
        }
    }
}

/// Run a command and capture stdout, returning it on success.
pub fn cmd_output(cmd: &[String]) -> Option<String> {
    let mut command = Command::new(&cmd[0]);
    if cmd.len() > 1 {
        command.args(&cmd[1..]);
    }
    match command.output() {
        Ok(out) if out.status.success() => Some(String::from_utf8_lossy(&out.stdout).trim().to_string()),
        _ => None,
    }
}

/// Escape a shell argument for `sh -lc` usage.
pub fn shell_escape(arg: &str) -> String {
    if arg.chars().all(|c| c.is_ascii_alphanumeric() || "-._/".contains(c)) {
        return arg.to_string();
    }
    let escaped = arg.replace('\'', "'\\''");
    format!("'{}'", escaped)
}

/// Pretty-print JSON to stdout.
pub fn print_json(value: &serde_json::Value) {
    let _ = std::io::stdout().write_all(serde_json::to_string_pretty(value).unwrap().as_bytes());
    let _ = std::io::stdout().write_all(b"\n");
}

/// Prompt the user to confirm applying a patch.
pub fn confirm_apply() -> bool {
    if !std::io::stdin().is_terminal() {
        return false;
    }
    eprint!("Apply patch? [y/N] ");
    let _ = std::io::stdout().flush();
    let mut input = String::new();
    if std::io::stdin().read_line(&mut input).is_ok() {
        let s = input.trim().to_lowercase();
        return s == "y" || s == "yes";
    }
    false
}