Crate thistermination

source ·
Expand description

thistermination

thistermination is a library crate inspired by thiserror to add the std::process::Termination trait to error enums.

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 traits std::process::Termination and std::fmt::Debug. The exit_code defaults to libc::EXIT_FAILURE, and the Debug message is the same as the Display message unless explicitly set using exit_code and msg.

    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 traits std::process::Termination, std::fmt::Debug, std::fmt::Display, and std::error::Error. The exit_code defaults to libc::EXIT_FAILURE, and msg 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 the std::process::Termination trait. If no exit_code is provided, it defaults to libc::EXIT_FAILURE. However, the std::fmt::Debug trait is necessary for the enum to be returned by the main 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 a std::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 and msg 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},
    }

Derive Macros