use std::borrow::Cow;
use indexmap::IndexMap;
use knope_config::{Template, Variable};
use miette::Diagnostic;
use tracing::info;
use crate::{RunType, state::State, variables, variables::replace_variables};
pub(crate) fn run_command(
state: RunType<State>,
mut command: String,
shell: bool,
variables: Option<IndexMap<Cow<'static, str>, Variable>>,
) -> Result<RunType<State>, Error> {
let (run_type, mut state) = state.take();
command = replace_variables(&Template::new(command, variables), &mut state)?;
if let RunType::DryRun(()) = run_type {
info!("Would run {command}");
return Ok(run_type.of(state));
}
let status = if shell {
execute::shell(command).status()?
} else {
execute::command(command).status()?
};
if status.success() {
return Ok(run_type.of(state));
}
Err(Error::Command(status))
}
#[derive(Debug, Diagnostic, thiserror::Error)]
pub(crate) enum Error {
#[error("Command returned non-zero exit code")]
#[diagnostic(
code(command::failed),
help("The command failed to execute. Try running it manually to get more information.")
)]
Command(std::process::ExitStatus),
#[error("I/O error: {0}")]
#[diagnostic(code(command::io))]
Io(#[from] std::io::Error),
#[error(transparent)]
#[diagnostic(transparent)]
Variables(#[from] variables::Error),
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test_run_command {
use super::*;
use crate::State;
#[test]
fn test() {
let command = "echo \"hello\"";
let result = run_command(
RunType::Real(State::new(
None,
None,
None,
Vec::new(),
Vec::new(),
Vec::new(),
)),
command.to_string(),
false,
None,
);
assert!(result.is_ok());
let result = run_command(
RunType::Real(State::new(
None,
None,
None,
Vec::new(),
Vec::new(),
Vec::new(),
)),
String::from("exit 1"),
false,
None,
);
assert!(result.is_err());
}
}