use std::env;
use std::error::Error;
use std::fmt;
use std::path;
use std::process;
pub trait CommandCargoExt
where
Self: Sized,
{
fn cargo_bin<S: AsRef<str>>(name: S) -> Result<Self, CargoError>;
}
impl CommandCargoExt for crate::cmd::Command {
fn cargo_bin<S: AsRef<str>>(name: S) -> Result<Self, CargoError> {
crate::cmd::Command::cargo_bin(name)
}
}
impl CommandCargoExt for process::Command {
fn cargo_bin<S: AsRef<str>>(name: S) -> Result<Self, CargoError> {
cargo_bin_cmd(name)
}
}
pub(crate) fn cargo_bin_cmd<S: AsRef<str>>(name: S) -> Result<process::Command, CargoError> {
let path = cargo_bin(name);
if path.is_file() {
Ok(process::Command::new(path))
} else {
Err(CargoError::with_cause(NotFoundError { path }))
}
}
#[derive(Debug)]
pub struct CargoError {
cause: Option<Box<dyn Error + Send + Sync + 'static>>,
}
impl CargoError {
pub fn with_cause<E>(cause: E) -> Self
where
E: Error + Send + Sync + 'static,
{
let cause = Box::new(cause);
Self { cause: Some(cause) }
}
}
impl Error for CargoError {}
impl fmt::Display for CargoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref cause) = self.cause {
writeln!(f, "Cause: {}", cause)?;
}
Ok(())
}
}
#[derive(Debug)]
struct NotFoundError {
path: path::PathBuf,
}
impl Error for NotFoundError {}
impl fmt::Display for NotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Cargo command not found: {}", self.path.display())
}
}
fn target_dir() -> path::PathBuf {
env::current_exe()
.ok()
.map(|mut path| {
path.pop();
if path.ends_with("deps") {
path.pop();
}
path
})
.unwrap()
}
pub fn cargo_bin<S: AsRef<str>>(name: S) -> path::PathBuf {
cargo_bin_str(name.as_ref())
}
fn cargo_bin_str(name: &str) -> path::PathBuf {
let env_var = format!("CARGO_BIN_EXE_{}", name);
std::env::var_os(&env_var)
.map(|p| p.into())
.unwrap_or_else(|| target_dir().join(format!("{}{}", name, env::consts::EXE_SUFFIX)))
}