use anyhow::{bail, Result};
use std::{
env, fs,
path::{Path, PathBuf},
process,
};
pub(crate) fn execute_external_subcommand(args: Vec<String>) -> Result<process::Output> {
let cmd = args.get(0).expect("`args` must not be empty");
let args = &args[1..];
let path = find_external_subcommand(cmd);
let command = match path {
Some(command) => command,
None => bail!("no such subcommand: `{}`", cmd),
};
let output = process::Command::new(command)
.stdin(process::Stdio::inherit())
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
.args(args)
.output()?;
Ok(output)
}
fn find_external_subcommand(cmd: &str) -> Option<PathBuf> {
let command_exe = format!("forc-{}{}", cmd, env::consts::EXE_SUFFIX);
search_directories()
.iter()
.map(|dir| dir.join(&command_exe))
.find(|file| is_executable(file))
}
fn search_directories() -> Vec<PathBuf> {
if let Some(val) = env::var_os("PATH") {
return env::split_paths(&val).collect();
}
vec![]
}
#[cfg(unix)]
fn is_executable(path: &Path) -> bool {
use std::os::unix::prelude::*;
fs::metadata(path)
.map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o111 != 0)
.unwrap_or(false)
}
#[cfg(windows)]
fn is_executable(path: &Path) -> bool {
path.is_file()
}
fn is_plugin(path: &Path) -> bool {
if let Some(stem) = path.file_name().and_then(|os_str| os_str.to_str()) {
if stem.starts_with("forc-") && is_executable(path) {
return true;
}
}
false
}
pub(crate) fn find_all() -> impl Iterator<Item = PathBuf> {
search_directories()
.into_iter()
.flat_map(walkdir::WalkDir::new)
.filter_map(Result::ok)
.map(|entry| entry.path().to_path_buf())
.filter(|p| is_plugin(p))
}