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}