command_error/
output_context.rs

1use std::borrow::Borrow;
2use std::fmt::Debug;
3use std::fmt::Display;
4use std::process::ExitStatus;
5
6#[cfg(doc)]
7use std::process::Command;
8#[cfg(doc)]
9use std::process::Output;
10
11#[cfg(doc)]
12use crate::CommandExt;
13
14use crate::CommandDisplay;
15use crate::Error;
16use crate::OutputError;
17use crate::OutputLike;
18
19/// [`Output`] combined with context about the [`Command`] that produced it.
20///
21/// This contains additional information about the command that was run (via a [`CommandDisplay`]
22/// object) and can be used to construct error messages (for use in methods like
23/// [`CommandExt::output_checked_as`]).
24///
25/// Note that because [`ExitStatus`] has a trivial implementation for [`OutputLike`] (where
26/// [`stdout`][OutputLike::stdout] and [`stderr`][OutputLike::stderr] return empty strings), this
27/// is also used as context for [`status`][`CommandExt::status_checked`] calls.
28pub struct OutputContext<O> {
29    output: O,
30    command: Box<dyn CommandDisplay + Send + Sync>,
31}
32
33impl<O> OutputContext<O>
34where
35    O: OutputLike + Send + Sync + 'static,
36{
37    /// Construct a new [`OutputContext`].
38    pub fn new(output: O, command: Box<dyn CommandDisplay + Send + Sync>) -> Self {
39        Self { output, command }
40    }
41
42    /// Get the [`OutputLike`] data contained in this context object.
43    pub fn into_output(self) -> O {
44        self.output
45    }
46
47    /// Get a reference to the [`OutputLike`] data contained in this context object.
48    pub fn output(&self) -> &O {
49        &self.output
50    }
51
52    /// Get the command's [`ExitStatus`].
53    pub fn status(&self) -> ExitStatus {
54        self.output.status()
55    }
56
57    /// Get a reference to the command contained in this context object, for use in error messages
58    /// or diagnostics.
59    pub fn command(&self) -> &(dyn CommandDisplay + Send + Sync) {
60        self.command.borrow()
61    }
62
63    /// Get the command contained in this context object, for use in error messages
64    /// or diagnostics.
65    pub fn into_command(self) -> Box<dyn CommandDisplay> {
66        self.command
67    }
68
69    /// Get the output and command contained in this context object.
70    ///
71    /// Unlike [`OutputContext::into_output`] and [`OutputContext::into_command`], this lets you
72    /// extract both fields.
73    pub fn into_output_and_command(self) -> (O, Box<dyn CommandDisplay>) {
74        (self.output, self.command)
75    }
76
77    /// Construct an error that indicates this command failed, containing information about the
78    /// command and its output.
79    ///
80    /// See [`CommandExt`] for examples of the error format.
81    ///
82    /// This is a thin wrapper around [`OutputContext::output_error`].
83    pub fn error(self) -> Error {
84        Error::from(self.output_error())
85    }
86
87    /// Construct an error that indicates this command failed, containing information about the
88    /// command and its output.
89    ///
90    /// This is like [`OutputContext::error`], but it returns the inner [`OutputError`] directly,
91    /// rather than wrapping it in an [`Error`].
92    pub fn output_error(self) -> OutputError {
93        OutputError::new(self.command, Box::new(self.output))
94    }
95
96    /// Construct an error that indicates this command failed, containing information about the
97    /// command, its output, and the provided error message.
98    ///
99    /// See [`CommandExt::output_checked_as`] for examples of the error format.
100    pub fn error_msg<E>(self, message: E) -> Error
101    where
102        E: Debug + Display + Send + Sync + 'static,
103    {
104        Error::from(
105            OutputError::new(self.command, Box::new(self.output)).with_message(Box::new(message)),
106        )
107    }
108
109    pub(crate) fn maybe_error_msg<E>(self, message: Option<E>) -> Error
110    where
111        E: Debug + Display + Send + Sync + 'static,
112    {
113        let ret = self.output_error();
114        Error::from(match message {
115            Some(message) => ret.with_message(Box::new(message)),
116            None => ret,
117        })
118    }
119}