use super::errors;
use super::eval;
use super::model;
pub fn run<S>(cmd: &[S]) -> Result<(), errors::GardenError>
where
S: AsRef<std::ffi::OsStr>,
{
let mut exit_status = errors::EX_ERROR;
if let Ok(mut p) = subprocess::Popen::create(cmd, subprocess::PopenConfig::default()) {
exit_status = status(p.wait());
}
result_from_exit_status(exit_status)
}
pub fn result_from_exit_status(exit_status: i32) -> Result<(), errors::GardenError> {
match exit_status {
errors::EX_OK => Ok(()),
_ => Err(errors::GardenError::ExitStatus(exit_status)),
}
}
pub fn status(result: subprocess::Result<subprocess::ExitStatus>) -> i32 {
let mut exit_status = errors::EX_ERROR;
if let Ok(status_result) = result {
match status_result {
subprocess::ExitStatus::Exited(status) => {
exit_status = status as i32;
}
subprocess::ExitStatus::Signaled(status) => {
exit_status = status as i32;
}
subprocess::ExitStatus::Other(status) => {
exit_status = status;
}
_ => (),
}
}
exit_status
}
pub fn trim_stdout(capture: &subprocess::CaptureData) -> String {
capture.stdout_str().trim_end().into()
}
fn command_error_from_popen_error(
command: String,
popen_err: subprocess::PopenError,
) -> errors::CommandError {
let status = match popen_err {
subprocess::PopenError::IoError(err) => err.raw_os_error().unwrap_or(1),
_ => 1,
};
errors::CommandError::ExitStatus { command, status }
}
pub fn capture_stdout(
exec: subprocess::Exec,
) -> Result<subprocess::CaptureData, errors::CommandError> {
let command = exec.to_cmdline_lossy();
exec.stdout(subprocess::Redirection::Pipe)
.capture()
.map_err(|popen_err| command_error_from_popen_error(command, popen_err))
}
pub fn capture(exec: subprocess::Exec) -> Result<subprocess::CaptureData, errors::CommandError> {
let command = exec.to_cmdline_lossy();
exec.stdout(subprocess::Redirection::Pipe)
.stderr(subprocess::Redirection::Pipe)
.capture()
.map_err(|popen_err| command_error_from_popen_error(command, popen_err))
}
pub fn exec_cmd<S>(command: &[S]) -> subprocess::Exec
where
S: AsRef<std::ffi::OsStr>,
{
if command.len() > 1 {
subprocess::Exec::cmd(&command[0]).args(&command[1..])
} else {
subprocess::Exec::cmd(&command[0])
}
}
pub fn exec_in_dir<P, S>(command: &[S], path: P) -> subprocess::Exec
where
P: AsRef<std::path::Path>,
S: AsRef<std::ffi::OsStr>,
{
exec_cmd(command).cwd(path)
}
pub fn exec_in_context<S>(
config: &mut model::Configuration,
context: &model::TreeContext,
quiet: bool,
verbose: u8,
command: &[S],
) -> Result<(), errors::GardenError>
where
S: AsRef<std::ffi::OsStr>,
{
let path;
{
let tree = &config.trees[context.tree];
path = tree.path_as_ref()?.clone();
if !model::print_tree(tree, verbose, quiet) {
return Ok(());
}
}
let env = eval::environment(config, context);
let command_vec = resolve_command(command, &env);
let mut exec = exec_in_dir(&command_vec, path);
for (name, value) in &env {
exec = exec.env(name, value);
}
result_from_exit_status(status(exec.join()))
}
fn resolve_command<S>(command: &[S], env: &[(String, String)]) -> Vec<String>
where
S: AsRef<std::ffi::OsStr>,
{
let mut cmd_path = std::path::PathBuf::from(&command[0]);
if !cmd_path.is_absolute() {
for (name, value) in env {
if name == "PATH" {
if let Some(path_buf) = std::env::split_paths(&value).find_map(|dir| {
let full_path = dir.join(&cmd_path);
if full_path.is_file() {
Some(full_path)
} else {
None
}
}) {
cmd_path = path_buf;
}
break;
}
}
}
let mut command_vec: Vec<String> = Vec::new();
command_vec.reserve(command.len());
command_vec.push(cmd_path.to_string_lossy().to_string());
for arg in &command[1..] {
let curpath = std::path::PathBuf::from(arg);
command_vec.push(curpath.to_string_lossy().into());
}
command_vec
}
pub fn current_exe() -> String {
match std::env::current_exe() {
Err(_) => "garden".into(),
Ok(path) => path.to_string_lossy().into(),
}
}