use std::{
ffi::{OsStr, OsString},
fmt,
path::PathBuf,
process::{Command, ExitStatus},
};
use anyhow::Error;
use pico_args::Arguments;
#[derive(Debug)]
struct ExecError {
program: OsString,
args: Vec<OsString>,
status: ExitStatus,
}
impl<'a> fmt::Display for ExecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"subprocess {} failed with status {}",
self.program.to_string_lossy(),
self.status
)
}
}
impl std::error::Error for ExecError {}
#[derive(Debug)]
pub struct DisplayArgs<T>(pub T);
impl<T> fmt::Display for DisplayArgs<T>
where
T: Iterator + Clone,
T::Item: AsRef<OsStr>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut args = self.0.clone();
let mut next = args.next();
while let Some(arg) = next {
let s = arg.as_ref().to_string_lossy();
write!(f, "{}", s)?;
next = args.next();
if next.is_some() {
write!(f, " ")?;
}
}
Ok(())
}
}
pub fn empty_env() -> Vec<(&'static str, &'static OsStr)> {
Vec::new()
}
pub fn cmd<P, E, A, V>(program: P, args: A, env: E, verbose: bool) -> Result<(), Error>
where
P: AsRef<OsStr>,
A: IntoIterator,
A::IntoIter: Clone,
A::Item: AsRef<OsStr>,
E: IntoIterator<Item = (&'static str, V)>,
V: AsRef<OsStr>,
{
let program = program.as_ref();
let args = args.into_iter();
let env = env.into_iter();
if verbose {
println!(
"* {} {}",
program.to_string_lossy(),
DisplayArgs(args.clone())
);
}
let status = Command::new(program)
.args(args.clone())
.envs(env)
.status()?;
if !status.success() {
return Err(ExecError {
program: program.to_owned(),
args: args.map(|arg| arg.as_ref().to_owned()).collect(),
status,
}
.into());
}
Ok(())
}
pub fn get_prefix_opt(args: &mut Arguments) -> anyhow::Result<PathBuf> {
match args.opt_value_from_str("--prefix")? {
Some(prefix) => Ok(prefix),
None => {
let mut home = PathBuf::from(std::env::var("HOME")?);
home.push(".local");
Ok(home)
}
}
}