2 stable releases
1.1.0 | Oct 8, 2023 |
---|---|
1.0.0 | Oct 5, 2023 |
#2550 in Rust patterns
40KB
492 lines
thistermination
thistermination is a library crate inspired by thiserror to add the std::process::Termination
trait to error enums.
[dependencies]
thistermination = "1.0"
Compiler support: requires rustc 1.56+
Why Implement The Termination Trait?
A struct or enum that implements the std::process::Termination
and std::fmt::Debug
traits can be returned by the main
function, allowing developers to print a message on program exit and set the exit code.
Usage
To add the std::process::Termination
trait to an enum, you can use one of three possible derive macros:
-
#[derive(Termination)]
: is intended to be used in combination with thiserror, this macro implements the traitsstd::process::Termination
andstd::fmt::Debug
. Theexit_code
defaults tolibc::EXIT_FAILURE
, and the Debug message is the same as the Display message unless explicitly set usingexit_code
andmsg
.use thistermination::{Termination}; use thiserror::Error; #[derive(Error, Termination)] pub enum RequestError { #[error("request failed {0:?}")] RequestFailed(#[from] reqwest::Error), #[termination(msg("exiting wrong api key"))] #[error("wrong api key")] WrongAPIKey, #[termination(exit_code(3))] #[error("failed with status {0}")] RequestStatusError(u16), #[termination(exit_code(4), msg("exiting failed to load image {error:?}"))] #[error("failed to load image {error:?}")] ImageLoadError{#[from] error: image::ImageError}, } fn main() -> Result<(), RequestError> { Err(RequestError::WrongAPIKey) }
-
#[derive(TerminationFull)]
: is intended to be used without thiserror, this macro implements the traitsstd::process::Termination
,std::fmt::Debug
,std::fmt::Display
, andstd::error::Error
. Theexit_code
defaults tolibc::EXIT_FAILURE
, andmsg
is required and used for both Display and Debug.use thistermination::{TerminationFull}; #[derive(TerminationFull)] pub enum RequestError { #[termination(exit_code(1), msg("request failed {0:?}"))] RequestFailed(#[from] reqwest::Error), #[termination(exit_code(2), msg("wrong api key"))] WrongAPIKey, #[termination(exit_code(3), msg("failed with status {0}"))] RequestStatusError(u16), #[termination(exit_code(4), msg("failed to load image {error:?}"))] ImageLoadError{#[from] error: image::ImageError}, } fn main() -> Result<(), RequestError> { Err(RequestError::WrongAPIKey) }
-
#[derive(TerminationNoDebug)]
: is the most basic variant, implementing only thestd::process::Termination
trait. If noexit_code
is provided, it defaults tolibc::EXIT_FAILURE
. However, thestd::fmt::Debug
trait is necessary for the enum to be returned by themain
function and must be implemented manually or using the Debug macro.use thistermination::{TerminationNoDebug}; #[derive(TerminationNoDebug, Debug)] pub enum RequestError { #[termination(exit_code(1))] RequestFailed(reqwest::Error), WrongAPIKey, #[termination(exit_code(3))] RequestStatusError(u16), ImageLoadError{error: image::ImageError}, } fn main() -> Result<(), RequestError> { Err(RequestError::WrongAPIKey) }
Details
-
thistermination does not appear in your public API; the macros simply implement the the various traits.
-
The macros can be derived for unit enums, enums with named fields, and enum tuples.
-
msg
supports accessing the fields of the enum in a format string manner#[termination(msg("{var}"))]
⟶write!("{}", self.var)
#[termination(msg("{0}"))]
⟶write!("{}", self.0)
#[termination(msg("{var:?}"))]
⟶write!("{:?}", self.var)
#[termination(msg("{0:?}"))]
⟶write!("{:?}", self.0)
You can also specify additional format string arguments for
msg
#[derive(TerminationFull)] pub enum RequestError { #[termination(exit_code(4), msg("failed to load image {0:?}"))] ImageLoadError(#[from] image::ImageError), }
-
Using
#[from]
will generate astd::convert::From
implementation for the specific variant. A variant with#[from]
is not allowed to contain any additional fields and can only be used in combination with#[derive(TerminationFull)]
.#[derive(TerminationFull)] pub enum CLIError { #[termination(exit_code(4), msg("Invalid argument {0}, expected < {}", i16::MAX))] InvalidArgument(u16), }
-
You can also change the default values of
exit_code
andmsg
by adding the#[termination(...)]
helper attribute to the enum itself.#[derive(Error, Termination)] #[termination(exit_code(3), msg("Fatal Error"))] pub enum RequestError { #[error("request failed {0:?}")] RequestFailed(#[from] reqwest::Error), #[error("wrong api key")] WrongAPIKey, #[error("failed with status {0}")] RequestStatusError(u16), #[termination(exit_code(4), msg("exiting failed to load image {error:?}"))] #[error("failed to load image {error:?}")] ImageLoadError{#[from] error: image::ImageError}, }
Comparison To thiserror
#[derive(TerminationFull)]
can be used instead of thiserror as it offers many of the basic features of thiserror. However, it lacks some features like #[source]
, #[backtrace]
, the ability to automatically detect a backtrace, and #[error(transparent)]
. If any of these features are required, you can use thiserror in combination with #[derive(Termination)]
.
Dependencies
~2.5–4MB
~72K SLoC