use std::fmt::Debug;
use std::fmt::Display;
use std::process::Child;
use std::process::ExitStatus;
use std::process::Output;
use utf8_command::Utf8Output;
use crate::ChildContext;
#[cfg(doc)]
use crate::CommandExt;
use crate::Error;
use crate::ExecError;
use crate::OutputContext;
use crate::OutputConversionError;
use crate::OutputLike;
use crate::TryWaitContext;
use crate::WaitError;
pub trait ChildExt: Sized {
type Error: From<Error>;
#[track_caller]
fn output_checked_as<O, R, E>(
self,
succeeded: impl Fn(OutputContext<O>) -> Result<R, E>,
) -> Result<R, E>
where
O: Debug,
O: OutputLike,
O: 'static,
O: Send,
O: Sync,
O: TryFrom<Output>,
<O as TryFrom<Output>>::Error: Display + Send + Sync,
E: From<Self::Error>;
#[track_caller]
fn output_checked_with<O, E>(
self,
succeeded: impl Fn(&O) -> Result<(), Option<E>>,
) -> Result<O, Self::Error>
where
O: Debug,
O: OutputLike,
O: TryFrom<Output>,
<O as TryFrom<Output>>::Error: Display + Send + Sync,
O: Send + Sync + 'static,
E: Debug,
E: Display,
E: Send + Sync + 'static,
{
self.output_checked_as(|context| match succeeded(context.output()) {
Ok(()) => Ok(context.into_output()),
Err(user_error) => Err(context.maybe_error_msg(user_error).into()),
})
}
#[track_caller]
fn output_checked(self) -> Result<Output, Self::Error> {
self.output_checked_with(|output: &Output| {
if output.status.success() {
Ok(())
} else {
Err(None::<String>)
}
})
}
#[track_caller]
fn output_checked_utf8(self) -> Result<Utf8Output, Self::Error> {
self.output_checked_with_utf8(|output| {
if output.status.success() {
Ok(())
} else {
Err(None::<String>)
}
})
}
#[track_caller]
fn output_checked_with_utf8<E>(
self,
succeeded: impl Fn(&Utf8Output) -> Result<(), Option<E>>,
) -> Result<Utf8Output, Self::Error>
where
E: Display,
E: Debug,
E: Send + Sync + 'static,
{
self.output_checked_with(succeeded)
}
#[track_caller]
fn try_wait_checked_as<R, E>(
&mut self,
succeeded: impl Fn(TryWaitContext) -> Result<R, E>,
) -> Result<R, E>
where
E: From<Self::Error>;
#[track_caller]
fn try_wait_checked(&mut self) -> Result<Option<ExitStatus>, Self::Error> {
self.try_wait_checked_as(|context| match context.into_output_context() {
Some(context) => {
if context.status().success() {
Ok(Some(context.status()))
} else {
Err(context.error().into())
}
}
None => Ok(None),
})
}
#[track_caller]
fn wait_checked_as<R, E>(
&mut self,
succeeded: impl Fn(OutputContext<ExitStatus>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<Self::Error>;
#[track_caller]
fn wait_checked_with<E>(
&mut self,
succeeded: impl Fn(ExitStatus) -> Result<(), Option<E>>,
) -> Result<ExitStatus, Self::Error>
where
E: Debug,
E: Display,
E: Send + Sync + 'static,
{
self.wait_checked_as(|context| match succeeded(context.status()) {
Ok(()) => Ok(context.status()),
Err(user_error) => Err(context.maybe_error_msg(user_error).into()),
})
}
#[track_caller]
fn wait_checked(&mut self) -> Result<ExitStatus, Self::Error> {
self.wait_checked_with(|status| {
if status.success() {
Ok(())
} else {
Err(None::<String>)
}
})
}
fn log(&self) -> Result<(), Self::Error>;
}
impl ChildExt for ChildContext<Child> {
type Error = Error;
fn output_checked_as<O, R, E>(
self,
succeeded: impl Fn(OutputContext<O>) -> Result<R, E>,
) -> Result<R, E>
where
O: Debug,
O: OutputLike,
O: 'static,
O: Send,
O: Sync,
O: TryFrom<Output>,
<O as TryFrom<Output>>::Error: Display + Send + Sync,
E: From<Self::Error>,
{
self.log()?;
let (child, command) = self.into_child_and_command();
match child.wait_with_output() {
Ok(output) => match output.try_into() {
Ok(output) => succeeded(OutputContext::new(output, command)),
Err(error) => {
Err(Error::from(OutputConversionError::new(command, Box::new(error))).into())
}
},
Err(inner) => Err(Error::from(ExecError::new(command, inner)).into()),
}
}
fn try_wait_checked_as<R, E>(
&mut self,
succeeded: impl Fn(TryWaitContext) -> Result<R, E>,
) -> Result<R, E>
where
E: From<Self::Error>,
{
let command = self.command_boxed().clone();
match self.child_mut().try_wait() {
Ok(status) => succeeded(TryWaitContext::new(status, command)),
Err(inner) => Err(Error::from(WaitError::new(command, inner)).into()),
}
}
fn wait_checked_as<R, E>(
&mut self,
succeeded: impl Fn(OutputContext<ExitStatus>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<Self::Error>,
{
self.log()?;
let command = self.command_boxed().clone();
match self.child_mut().wait() {
Ok(status) => succeeded(OutputContext::new(status, command)),
Err(inner) => Err(Error::from(ExecError::new(command, inner)).into()),
}
}
fn log(&self) -> Result<(), Self::Error> {
#[cfg(feature = "tracing")]
{
tracing::debug!(command = %self.command(), "Executing command");
}
Ok(())
}
}