use std::env;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
const OUTPUT_TAIL_BYTES: usize = 4096;
#[derive(Debug)]
pub(crate) struct CapturedToolFailure {
pub(crate) status: String,
pub(crate) stdout: String,
pub(crate) stderr: String,
}
pub(crate) fn run_captured(mut command: Command) -> io::Result<Option<CapturedToolFailure>> {
let output = command.output()?;
if output.status.success() {
return Ok(None);
}
Ok(Some(CapturedToolFailure {
status: output.status.to_string(),
stdout: output_tail(&output.stdout),
stderr: output_tail(&output.stderr),
}))
}
pub(crate) fn find_tool(
override_path: Option<&Path>,
name: &str,
fallback_dir: Option<&Path>,
) -> Option<PathBuf> {
if let Some(path) = override_path {
return Some(path.to_path_buf());
}
find_on_path(name).or_else(|| {
let fallback_dir = fallback_dir?;
let path = fallback_dir.join(name);
path.is_file().then_some(path)
})
}
fn find_on_path(name: &str) -> Option<PathBuf> {
env::var_os("PATH").and_then(|path| {
env::split_paths(&path)
.map(|dir| dir.join(name))
.find(|path| path.is_file())
})
}
fn output_tail(bytes: &[u8]) -> String {
let tail_start = bytes.len().saturating_sub(OUTPUT_TAIL_BYTES);
let mut output = String::new();
if tail_start > 0 {
output.push_str("[truncated]\n");
}
output.push_str(&String::from_utf8_lossy(&bytes[tail_start..]));
output
}