use anyhow::{anyhow, Result};
use regex::Regex;
use std::process::{Command as StdCommand, Stdio};
pub async fn run_capture_async(
cmd: &str,
args: &[&str],
) -> Result<String> {
let output = tokio::process::Command::new(cmd)
.args(args)
.output()
.await?;
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr);
return Err(anyhow!(
"{} {:?} failed: {}",
cmd,
args,
err.trim()
));
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub async fn run_quiet_async(
cmd: &str,
args: &[&str],
_label: &str,
) -> Result<()> {
let status = tokio::process::Command::new(cmd)
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::inherit())
.status()
.await?;
if !status.success() {
return Err(anyhow!("{} {:?} failed", cmd, args));
}
Ok(())
}
pub async fn run_status_async(cmd: &str, args: &[&str]) -> bool {
tokio::process::Command::new(cmd)
.args(args)
.status()
.await
.map(|s| s.success())
.unwrap_or(false)
}
pub fn run_capture(cmd: &str, args: &[&str]) -> Result<String> {
let output = StdCommand::new(cmd).args(args).output()?;
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr);
return Err(anyhow!(
"{} {:?} failed: {}",
cmd,
args,
err.trim()
));
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn run_quiet(
cmd: &str,
args: &[&str],
_label: &str,
) -> Result<()> {
let status = StdCommand::new(cmd)
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::inherit())
.status()?;
if !status.success() {
return Err(anyhow!("{} {:?} failed", cmd, args));
}
Ok(())
}
pub fn run_status(cmd: &str, args: &[&str]) -> bool {
StdCommand::new(cmd)
.args(args)
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn render_template(tpl: &str, pairs: &[(&str, &str)]) -> String {
let re = Regex::new(r"\{\{\s*([a-zA-Z0-9_]+)\s*\}\}").unwrap();
let mut map: std::collections::HashMap<&str, &str> =
std::collections::HashMap::new();
for (k, v) in pairs {
map.insert(*k, *v);
}
let replaced = re.replace_all(tpl, |caps: ®ex::Captures| {
let k = &caps[1];
map.get::<str>(k).copied().unwrap_or("")
});
let s = replaced.to_string();
let s = Regex::new(r"\n{3,}")
.unwrap()
.replace_all(&s, "\n\n")
.to_string();
let s = s.trim().to_string();
if s.is_empty() {
s
} else {
format!("{}\n", s)
}
}
pub async fn run_shell_interactive_async(
cmdline: &str,
) -> Result<()> {
let shell = crate::shell::detect_shell();
let sh = shell.to_string_lossy().to_string();
let status = tokio::process::Command::new(&sh)
.args(["-lc", cmdline])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.await?;
if !status.success() {
return Err(anyhow!("shell command failed: {}", cmdline));
}
Ok(())
}