explicit_error_exit/
lib.rs

1//! Built on top of [`explicit-error`](https://crates.io/crates/explicit-error), it provides idiomatic tools to manage errors that ends a process/program.
2//! Based on the [explicit-error](explicit_error) crate, its chore tenet is to favor explicitness by inlining the error output while remaining concise.
3//!
4//! The key features are:
5//! - Explicitly mark any error wrapped in a [Result] as a [Bug], a backtrace is captured.
6//! - Inline transformation of any errors wrapped in a [Result] into an [Error].
7//! - A derive macro [ExitError](derive::ExitError) to easily declare how enum or struct errors transform into an [Error].
8//! - Add context to errors to help debug.
9//!
10//! # A tour of explicit-error-bin
11//!
12//! The cornerstone of the library is the [Error] type. Use `Result<T, explicit_error_http::Error>`, or equivalently `explicit_error_bin::Result<T>`, as the return type of any faillible function returning errors that can end the program.
13//!
14//! ## Inline
15//!
16//! In the body of the function you can explicitly turn errors as exit errors using [ExitError] or marking them as [Bug].
17//! ```rust
18//! use explicit_error_exit::{prelude::*, ExitError, Result, Bug};
19//! use std::process::ExitCode;
20//! // Import the prelude to enable functions on std::result::Result
21//!
22//! fn business_logic() -> Result<()> {
23//!     Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))
24//!         .bug()?;
25//!     
26//!     // Same behavior as bug() but the error is not captured as a source because it does not implement `[std::error::Error]`
27//!     Err("error message").bug_no_source()?;
28//!
29//!     if 1 > 2 {
30//!         Err(Bug::new()
31//!             .with_context("Usefull context to help debug."))?;
32//!     }
33//!
34//!     Err(42).map_err(|e|
35//!         ExitError::new(
36//!             "Something went wrong because ..",
37//!             ExitCode::from(e)
38//!         )
39//!     )?;
40//!
41//!     Ok(())
42//! }
43//!```
44//!
45//! ## Enum and struct
46//!
47//! Domain errors are often represented as enum or struct as they are raised in different places.
48//! To easily enable the conversion to [Error] use the [ExitError](derive::ExitError) derive and implement `From<&MyError> for ExitError`.
49//!
50//! ```rust
51//! use explicit_error_exit::{prelude::*, ExitError, Result, derive::ExitError};
52//! use std::process::ExitCode;
53//!
54//! #[derive(ExitError, Debug)]
55//! enum MyError {
56//!     Foo,
57//! }
58//!
59//! impl From<&MyError> for ExitError {
60//!     fn from(value: &MyError) -> Self {
61//!         match value {
62//!             MyError::Foo => ExitError::new(
63//!                     "Something went wrong because ..",
64//!                     ExitCode::from(42)
65//!                 ),
66//!         }
67//!     }
68//! }
69//!
70//! fn business_logic() -> Result<()> {
71//!     Err(MyError::Foo)?;
72//!
73//!     Ok(())
74//! }
75//! ```
76//!
77//! Note: The [ExitError](derive::ExitError) derive implements the conversion to [Error], the impl of [Display](std::fmt::Display) and [std::error::Error].
78//!
79//! # Pattern matching
80//!
81//! One of the drawbacks of using one and only one return type for different domain functions is that callers loose the ability to pattern match on the returned error.
82//! A solution is provided using [try_map_on_source](explicit_error::ResultError::try_map_on_source) on any `Result<T, Error>`, or equivalently `explicit_error_exit::Result<T>`.
83//!
84//! ```rust
85//! use explicit_error_exit::{prelude::*, ExitError, Result, derive::ExitError};
86//! use std::process::ExitCode;
87//!
88//! #[derive(ExitError, Debug)]
89//! enum MyError {
90//!     Foo,
91//!     Bar
92//! }
93//!
94//! # impl From<&MyError> for ExitError {
95//! #     fn from(value: &MyError) -> Self {
96//! #         ExitError::new(
97//! #           "Something went wrong because ..",
98//! #           ExitCode::from(42))
99//! #     }
100//! # }
101//! fn business_logic() -> Result<()> {
102//!     let err: Result<()> = Err(MyError::Foo)?;
103//!
104//!     // Do the map if the source's type of the Error is MyError
105//!     err.try_map_on_source(|e| {
106//!         match e {
107//!             MyError::Foo => ExitError::new(
108//!                 "Foo",
109//!                 ExitCode::SUCCESS),
110//!             MyError::Bar => ExitError::new(
111//!                 "Bar",
112//!                 ExitCode::FAILURE),
113//!         }
114//!     })?;
115//!
116//!     Ok(())
117//! }
118//! ```
119//!
120//! Note: under the hood [try_map_on_source](explicit_error::ResultError::try_map_on_source) perform some downcasting.
121mod domain;
122mod error;
123
124pub use domain::*;
125pub use error::*;
126
127pub type Error = explicit_error::Error<DomainError>;
128pub type Result<T> = std::result::Result<T, Error>;
129
130/// Re-import from [explicit_error] crate.
131pub use explicit_error::Bug;
132
133pub mod prelude {
134    pub use crate::ResultDomainWithContext;
135    pub use explicit_error::prelude::*;
136}
137
138pub mod derive {
139    pub use explicit_error_derive::ExitError;
140}