summaryrefslogtreecommitdiff
path: root/src/exit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/exit.rs')
-rw-r--r--src/exit.rs54
1 files changed, 54 insertions, 0 deletions
diff --git a/src/exit.rs b/src/exit.rs
new file mode 100644
index 0000000..3c7b724
--- /dev/null
+++ b/src/exit.rs
@@ -0,0 +1,54 @@
+use std::{
+ error::Error,
+ io,
+ process::{ExitCode, Termination},
+};
+
+use crate::error::chain;
+
+/// Formats errors for display before exiting the program.
+///
+/// When this is used as the return type of a program's `main`, it can be used to capture any errors
+/// during the execution of the program and to display them in a readable format at exit time:
+///
+/// ```no_run
+/// # use std::{error::Error, io};
+/// fn main() -> pilcrow::cli::Exit<impl Error> {
+/// my_complicated_task().into()
+/// }
+///
+/// fn my_complicated_task() -> Result<(), impl Error> {
+/// Err(io::Error::other("stand-in for a real failure"))
+/// }
+/// ```
+///
+/// If constructed with an `Ok(())`, the resulting `Exit` indicates successful execution, and, when
+/// returned from `main`, will cause the program to exit with a successful exit status. If constructed
+/// with any `Err(…)` value, the resulting `Exit` indicates unsuccessful execution, and, when
+/// returned from `main`, will cause the program to print the error (along with its `source()`
+/// chain, if any) before exiting with an unsuccessful exit status.
+pub struct Exit<E>(pub Result<(), E>);
+
+impl<E> From<Result<(), E>> for Exit<E> {
+ fn from(result: Result<(), E>) -> Self {
+ Self(result)
+ }
+}
+
+impl<E> Termination for Exit<E>
+where
+ E: Error,
+{
+ fn report(self) -> ExitCode {
+ let Self(result) = self;
+ match result {
+ Ok(()) => ExitCode::SUCCESS,
+ Err(err) => {
+ // if we can't write the error message to stderr, there's nothing else we can do
+ // instead, and we're about to exit with a failure anyway.
+ let _ = chain::format(&mut io::stderr().lock(), &err);
+ ExitCode::FAILURE
+ }
+ }
+ }
+}