Created
January 29, 2021 21:04
-
-
Save benkay86/6f4fbe31c219fb0a99bba13735c52206 to your computer and use it in GitHub Desktop.
Replacement for structopt::clap::Error to override from_args() and avoid calls to std::process::exit().
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Custom error type to replace [`structopt::clap::Error`]. | |
//! By default [`structopt::StructOpt::from_args()`] calls `exit().`` | |
//! By default [`std::process::exit()`] does not unwind the stack. | |
//! This is bad: https://www.youtube.com/watch?v=zQC8T71Y8e4 | |
//! Use [`OptsError`] like this to override `from_args()` and avoid calls to | |
//! `std::process::exit()`: | |
//! | |
//! ```ignore | |
//! #[derive(StructOpt)] | |
//! #[structopt(...)] | |
//! struct Opts { | |
//! // ... your command line arguments ... | |
//! } | |
//! | |
//! // Override from_args() to use [`OptsError`]. | |
//! impl Opts { | |
//! pub fn from_args() -> Result<Opts, OptsError> { | |
//! match Opts::from_iter_safe(std::env::args()) { | |
//! Ok(opts) => Ok(opts), | |
//! Err(e) => Err(OptsError { error: e }) | |
//! } | |
//! } | |
//! } | |
//! ``` | |
/// Custom error type to return when command line parsing has gone wrong. | |
/// | |
/// The default behavior of [structopt]'s `from_args()` when it encounters an | |
/// error is to print out an error message with some usage hints using the | |
/// [`std::fmt::Display`] trait of [`structopt::clap::Error`] and then call | |
/// [`std::process::exit()`]. The problem with this approach is that `exit()` | |
/// doesn't unwind the stack. It is safer to instead use `from_iter_safe()` and | |
/// then propagate the `Error` to the called by returning an `Result`. | |
/// | |
/// Why not just use [`structopt::clap::Error`]? In the most common use case, | |
/// the error propagate all the way up to `main()` and the Rust compiler will | |
/// emit code to do something like `eprinteln!("Error: {:?}", error)` just | |
/// before the program terminates. The pretty-formatted help, version, or | |
/// error/usage message is generated by the [`std::fmt::Display`] trait for | |
/// `clap::Error`, whereas its [`std::fmt::Display`] trait would be quite | |
/// indecipherable to the end user! | |
/// | |
/// This custom error type stores an inner [`structopt::clap::Error`], which is | |
/// accessible via its public member `OptsError::error`. Its `Display` and | |
/// `Debug` traits both use VT100 ANSI escape codes to erase the "Error: " | |
/// message printed by Rust, since it is misleading when printing out | |
/// help/version information and redundant when printing out out pretty- | |
/// formatted error/usage information. It then renders `clap::Error` using its | |
/// `Display` trait regardless of whether `OptsError` is being rendered via | |
/// `Display` or `Debug`. | |
/// | |
/// TLDR; used to override structopt's `from_args()` to propagate errors through | |
/// `main()` instead of terminating early. | |
pub struct OptsError { | |
/// Underlying command line parsing error that caused this error. | |
pub error: structopt::clap::Error | |
} | |
impl std::fmt::Display for OptsError { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
// Use VT100 ANSI escape codes to erase current line and move cursor to the | |
// beginning of the line (on the left). | |
write!(f, "\x1B[2K\x1B[1000D{}", self.error) | |
} | |
} | |
impl std::fmt::Debug for OptsError { | |
// `Debug` trait changed from `clap::Error` to be the same as `Display`. | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
<OptsError as std::fmt::Display>::fmt(self, f) | |
} | |
} | |
impl std::error::Error for OptsError { | |
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | |
self.error.source() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment