#![cfg(feature = "which_problem")]
use crate::CmdError;
use std::ffi::OsString;
use std::process::Command;
use which_problem::Which;
#[cfg(feature = "which_problem")]
pub fn map_which_problem(
error: CmdError,
cmd: &mut Command,
path_env: Option<OsString>,
) -> CmdError {
match error {
CmdError::SystemError(name, error) => {
CmdError::SystemError(name, annotate_which_problem(error, cmd, path_env))
}
CmdError::NonZeroExitNotStreamed(_) | CmdError::NonZeroExitAlreadyStreamed(_) => error,
}
}
#[must_use]
#[cfg(feature = "which_problem")]
fn annotate_which_problem(
error: std::io::Error,
cmd: &mut Command,
path_env: Option<OsString>,
) -> std::io::Error {
let program = cmd.get_program().to_os_string();
let current_working_dir = cmd.get_current_dir().map(std::path::Path::to_path_buf);
let problem = Which {
cwd: current_working_dir,
program,
path_env,
..Which::default()
}
.diagnose();
let annotation = match problem {
Ok(details) => format!("\nSystem diagnostic information:\n\n{details}"),
Err(error) => {
format!("\nInternal error while gathering diagnostic information:\n\n{error}")
}
};
annotate_io_error(error, annotation)
}
#[must_use]
#[cfg(feature = "which_problem")]
fn annotate_io_error(source: std::io::Error, annotation: String) -> std::io::Error {
IoErrorAnnotation::new(source, annotation).into_io_error()
}
#[derive(Debug)]
#[cfg(feature = "which_problem")]
pub(crate) struct IoErrorAnnotation {
source: std::io::Error,
annotation: String,
}
#[cfg(feature = "which_problem")]
impl IoErrorAnnotation {
pub(crate) fn new(source: std::io::Error, annotation: String) -> Self {
Self { source, annotation }
}
pub(crate) fn into_io_error(self) -> std::io::Error {
std::io::Error::new(self.source.kind(), self)
}
}
#[cfg(feature = "which_problem")]
impl std::fmt::Display for IoErrorAnnotation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.source)?;
f.write_str(&self.annotation)?;
Ok(())
}
}
#[cfg(feature = "which_problem")]
impl std::error::Error for IoErrorAnnotation {
fn cause(&self) -> Option<&dyn std::error::Error> {
self.source()
}
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}