use std::error::Error as StdError;
use std::process::{Command, Stdio};
use std::str;
use std::any::Any;
use std::fmt::Debug;
use crate::common::print_debug;
use crate::envs;
pub fn tty(command: &str) {
print_debug(command);
let child = Command::new("bash")
.arg("-c")
.arg(command)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn();
match child {
Ok(mut child) => {
let _status = child.wait();
}
Err(e) => eprintln!("Error spawning command: {}", e),
}
}
pub fn cmd(command: &str, evaluate: Option<bool>) -> Result<String, String> {
let _command = if evaluate.unwrap_or(false) {
envs::eval(command)
} else {
command.to_string()
};
let output = Command::new("bash").arg("-c").arg(_command).output();
match output {
Ok(result) => {
if result.status.success() {
if let Ok(output_string) = String::from_utf8(result.stdout) {
Ok(output_string.trim().to_string())
} else {
Err("Error converting output to string".to_string())
}
} else {
Err(format!("Command failed with error: {:?}", result.status))
}
}
Err(err) => {
Err(format!("Error spawning subprocess: {}", err))
}
}
}
pub fn stdout(command: &str) -> Result<String, String> {
cmd(command, None)
}
pub fn run(argv: &[&str]) -> Result<String, String> {
let (bin, args) = argv.split_first().ok_or_else(|| "empty argv".to_string())?;
let output = Command::new(bin)
.args(args)
.output()
.map_err(|e| format!("spawning {}: {}", bin, e))?;
if output.status.success() {
String::from_utf8(output.stdout)
.map(|s| s.trim().to_string())
.map_err(|_| "output was not valid UTF-8".to_string())
} else {
Err(format!(
"{} failed: {}",
bin,
String::from_utf8_lossy(&output.stderr).trim()
))
}
}
pub fn run_tty_in(dir: Option<&str>, argv: &[&str]) {
let Some((bin, args)) = argv.split_first() else {
eprintln!("Error: empty argv");
return;
};
let mut command = Command::new(bin);
command
.args(args)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
if let Some(d) = dir {
command.current_dir(d);
}
match command.spawn() {
Ok(mut child) => {
let _ = child.wait();
}
Err(e) => eprintln!("Error spawning {}: {}", bin, e),
}
}
pub fn run_tty(argv: &[&str]) {
run_tty_in(None, argv);
}
pub fn zk0(last_arg: &str) -> Result<Option<String>, Box<dyn StdError>> {
let args = ["exec", "-i", "zk0", "bash", "-c", last_arg];
let output = Command::new("docker").args(args).output()?;
if output.status.success() {
let stdout = String::from_utf8(output.stdout)?.trim().to_string();
Ok(Some(stdout))
} else {
let stderr = String::from_utf8(output.stderr)?;
eprintln!("Error:\n{}", stderr);
Ok(None)
}
}
pub fn execute_fn<T: Debug + Any>(response: Result<T, Box<dyn StdError>>) {
match response {
Ok(result) => {
if let Some(option) = (&result as &dyn Any).downcast_ref::<Option<String>>() {
if let Some(value) = option {
println!("{}", value);
}
} else {
println!("Operation successful: {:?}", result);
}
}
Err(err) => {
eprintln!("Failed to fetch data: {}", err);
}
}
}
#[cfg(test)]
mod tests {
use super::run;
#[test]
fn run_executes_argv_without_a_shell() {
let out = run(&["printf", "%s", "a b$(echo X);c"]).expect("printf runs");
assert_eq!(out, "a b$(echo X);c");
}
#[test]
fn run_reports_error_on_missing_binary() {
assert!(run(&["zzz-no-such-binary-zzz"]).is_err());
}
}