#![cfg(feature = "command")]
use crate::IntoResult;
use std::{
fmt::{self, Display},
io,
process::{Child, ExitStatus, Output},
};
#[derive(Debug)]
pub enum Error {
SpawnFailed(io::Error),
WaitFailed(io::Error),
CommandFailed(ExitStatus),
CommandFailedWithOutput(Output),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SpawnFailed(err) => write!(f, "Failed to spawn child process: {}", err),
Self::WaitFailed(err) => write!(f, "Failed to wait for child process to exit: {}", err),
Self::CommandFailed(status) => {
write!(f, "Command didn't complete successfully, ")?;
if let Some(exit_code) = status.code() {
write!(f, "exiting with code {}.", exit_code)
} else {
write!(f, "but returned no exit code.")
}
}
Self::CommandFailedWithOutput(Output { status, stderr, .. }) => {
write!(f, "{} ", Self::CommandFailed(status.to_owned()))?;
if !stderr.is_empty() {
write!(f, "stderr contents: {}", String::from_utf8_lossy(stderr))
} else {
write!(f, "stderr was empty.")
}
}
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::WaitFailed(err)
}
}
impl From<ExitStatus> for Error {
fn from(status: ExitStatus) -> Self {
Self::CommandFailed(status)
}
}
impl From<Output> for Error {
fn from(output: Output) -> Self {
Self::CommandFailedWithOutput(output)
}
}
impl Error {
pub fn status(&self) -> Option<ExitStatus> {
if let Self::CommandFailed(status) = self {
Some(status.clone())
} else {
self.output().map(|output| output.status.clone())
}
}
pub fn output(&self) -> Option<&Output> {
if let Self::CommandFailedWithOutput(output) = self {
Some(output)
} else {
None
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl IntoResult<ExitStatus, Error> for ExitStatus {
fn into_result(self) -> Result<ExitStatus> {
if self.success() {
Ok(self)
} else {
Err(self.into())
}
}
}
impl IntoResult<Output, Error> for Output {
fn into_result(self) -> Result<Output> {
if self.status.success() {
Ok(self)
} else {
Err(self.into())
}
}
}
impl IntoResult<ExitStatus, Error> for io::Result<ExitStatus> {
fn into_result(self) -> Result<ExitStatus> {
self.map_err(Error::WaitFailed)
.and_then(ExitStatus::into_result)
}
}
impl IntoResult<Output, Error> for io::Result<Output> {
fn into_result(self) -> Result<Output> {
self.map_err(Error::WaitFailed)
.and_then(Output::into_result)
}
}
impl IntoResult<Child, Error> for io::Result<Child> {
fn into_result(self) -> Result<Child> {
self.map_err(Error::WaitFailed)
}
}