use std::process::{Command, ExitCode, Stdio};
use anyhow::{Context, Result};
use super::{
batch::{BatchMode, format_batch_debug, run_batch},
shell::shell_escape,
types::ResolvedExecution,
};
use crate::platform::node::{
REAL_NODE_ENV, SHIM_ACTIVE_ENV, path_with_real_node_priority, resolve_real_node_path,
};
pub fn run(exec: &ResolvedExecution, use_sfw: bool) -> Result<ExitCode> {
if let Some(mode) = BatchMode::from_internal_program(&exec.program) {
return run_batch(mode, &exec.args, &exec.cwd);
}
let (program, args, passthrough) = materialize(exec, use_sfw)?;
let mut command = Command::new(&program);
command
.args(&args)
.current_dir(&exec.cwd)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
if let Ok(real_node) = resolve_real_node_path() {
command.env(REAL_NODE_ENV, &real_node);
if let Some(path) = path_with_real_node_priority(&real_node, std::env::var_os("PATH")) {
command.env("PATH", path);
}
}
if passthrough {
command.env(SHIM_ACTIVE_ENV, "1");
}
let status = command
.status()
.with_context(|| format!("failed to execute {program}"))?;
Ok(exit_code_from_status(status.code()))
}
pub fn format_debug(exec: &ResolvedExecution, use_sfw: bool) -> Result<String> {
if let Some(mode) = BatchMode::from_internal_program(&exec.program) {
return Ok(format_batch_debug(mode, &exec.args));
}
let (program, args, _) = materialize(exec, use_sfw)?;
let rendered = std::iter::once(shell_escape(&program))
.chain(args.iter().map(|arg| shell_escape(arg)))
.collect::<Vec<_>>()
.join(" ");
Ok(rendered)
}
fn materialize(exec: &ResolvedExecution, use_sfw: bool) -> Result<(String, Vec<String>, bool)> {
let mut program = exec.program.clone();
let mut args = exec.args.clone();
let mut passthrough = exec.passthrough;
if exec.passthrough && exec.program == "node" {
let real = resolve_real_node_path()?;
program = real.to_string_lossy().to_string();
passthrough = true;
}
if use_sfw && !exec.passthrough {
let mut sfw_args = Vec::with_capacity(args.len() + 1);
sfw_args.push(program);
sfw_args.append(&mut args);
program = "sfw".to_string();
args = sfw_args;
passthrough = false;
}
Ok((program, args, passthrough))
}
fn exit_code_from_status(code: Option<i32>) -> ExitCode {
code.map_or_else(|| ExitCode::from(1), exit_code_from_code)
}
fn exit_code_from_code(code: i32) -> ExitCode {
let code = u8::try_from(code).unwrap_or(1);
ExitCode::from(code)
}