explicit_error_exit/
error.rs

1use std::{fmt::Display, process::ExitCode};
2
3use crate::{DomainError, Error};
4
5/// Representation of errors that ends a process/program.
6///
7/// [Error] implements `From<ExitError>`, use `?` and `.into()` in functions and closures to convert to the [Error::Domain](explicit_error::Error::Domain) variant.
8///
9/// Note: [ExitError] convert to [Error] by converting first to [DomainError].
10/// # Examples
11/// Domain errors that derive [ExitError](crate::derive::ExitError) must implement `From<#MyDomainError> for ExitError`.
12/// ```rust
13/// use explicit_error_exit::{prelude::*, ExitError, Result, derive::ExitError};
14/// use std::process::ExitCode;
15///
16/// #[derive(ExitError, Debug)]
17/// enum MyError {
18///     Foo,
19/// }
20///
21/// impl From<&MyError> for ExitError {
22///     fn from(value: &MyError) -> Self {
23///         match value {
24///             MyError::Foo => ExitError::new(
25///                     "Something went wrong because ..",
26///                     ExitCode::from(42)
27///                 ),
28///         }
29///     }
30/// }
31/// ```
32///
33/// Domain errors cannot require to be extracted in either a struct or enum variant.
34/// You can generate [Error::Domain](explicit_error::Error::Domain) variant with an [ExitError]
35/// ```rust
36/// use explicit_error_exit::{prelude::*, ExitError, Result, Fault};
37/// use std::process::ExitCode;
38///
39/// fn business_logic() -> Result<()> {
40///
41///     Err(42).map_err(|e|
42///         ExitError::new(
43///             "Something went wrong because ..",
44///             ExitCode::from(e)
45///         )
46///     )?;
47///
48///     Ok(())
49/// }
50/// ```
51#[derive(Debug)]
52pub struct ExitError {
53    pub message: String,
54    pub exit_code: ExitCode,
55    pub context: Option<String>,
56}
57
58impl ExitError {
59    /// Generate an [ExitError] without a context. To add a context
60    /// use [with_context](ExitError::with_context) afterwards.
61    /// # Examples
62    /// ```rust
63    /// use explicit_error_exit::ExitError;
64    /// use std::process::ExitCode;
65    ///
66    /// ExitError::new(
67    ///     "Something went wrong because ..",
68    ///     ExitCode::from(42)
69    /// );
70    /// ```
71    pub fn new(message: impl Display, exit_code: ExitCode) -> Self {
72        Self {
73            message: message.to_string(),
74            exit_code,
75            context: None,
76        }
77    }
78
79    /// Add a context to an [ExitError], override if one was set. The context appears in display
80    /// but not in the [Display] implementation.
81    /// # Examples
82    /// ```rust
83    /// use explicit_error_exit::ExitError;
84    /// use std::process::ExitCode;
85    ///
86    /// ExitError::new(
87    ///     "Something went wrong because ..",
88    ///     ExitCode::from(42)
89    /// ).with_context("The reason why it went wrong");
90    /// ```
91    pub fn with_context(mut self, context: impl Display) -> Self {
92        self.context = Some(context.to_string());
93        self
94    }
95}
96
97impl std::error::Error for ExitError {}
98
99impl Display for ExitError {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(f, "{}", self.message)
102    }
103}
104
105impl From<ExitError> for Error {
106    fn from(value: ExitError) -> Self {
107        Error::Domain(Box::new(DomainError {
108            output: value,
109            source: None,
110        }))
111    }
112}