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 { /// 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(pub Result<(), E>); impl From> for Exit { fn from(result: Result<(), E>) -> Self { Self(result) } } impl Termination for Exit 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 } } } }