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("error message").bug()?;
24//!
25//!     Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))
26//!         .bug_with_source()?; // Same behavior as bug() but capture the wrapped std::error::Error as a source
27//!
28//!     if 1 > 2 {
29//!         Err(Bug::new()
30//!             .with_context("Usefull context to help debug."))?;
31//!     }
32//!
33//!     Err(42).map_err(|e|
34//!         ExitError::new(
35//!             "Something went wrong because ..",
36//!             ExitCode::from(e)
37//!         )
38//!     )?;
39//!
40//!     Ok(())
41//! }
42//!```
43//!
44//! ## Enum and struct
45//!
46//! Domain errors are often represented as enum or struct as they are raised in different places.
47//! To easily enable the conversion to [Error] use the [ExitError](derive::ExitError) derive and implement `From<&MyError> for ExitError`.
48//!
49//! ```rust
50//! use explicit_error_exit::{prelude::*, ExitError, Result, derive::ExitError};
51//! use std::process::ExitCode;
52//!
53//! #[derive(ExitError, Debug)]
54//! enum MyError {
55//!     Foo,
56//! }
57//!
58//! impl From<&MyError> for ExitError {
59//!     fn from(value: &MyError) -> Self {
60//!         match value {
61//!             MyError::Foo => ExitError::new(
62//!                     "Something went wrong because ..",
63//!                     ExitCode::from(42)
64//!                 ),
65//!         }
66//!     }
67//! }
68//!
69//! fn business_logic() -> Result<()> {
70//!     Err(MyError::Foo)?;
71//!
72//!     Ok(())
73//! }
74//! ```
75//!
76//! Note: The [ExitError](derive::ExitError) derive implements the conversion to [Error], the impl of [Display](std::fmt::Display) and [std::error::Error].
77//!
78//! # Pattern matching
79//!
80//! 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.
81//! 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>`.
82//!
83//! ```rust
84//! use explicit_error_exit::{prelude::*, ExitError, Result, derive::ExitError};
85//! use std::process::ExitCode;
86//!
87//! #[derive(ExitError, Debug)]
88//! enum MyError {
89//!     Foo,
90//!     Bar
91//! }
92//!
93//! # impl From<&MyError> for ExitError {
94//! #     fn from(value: &MyError) -> Self {
95//! #         ExitError::new(
96//! #           "Something went wrong because ..",
97//! #           ExitCode::from(42))
98//! #     }
99//! # }
100//! fn business_logic() -> Result<()> {
101//!     let err: Result<()> = Err(MyError::Foo)?;
102//!
103//!     // Do the map if the source's type of the Error is MyError
104//!     err.try_map_on_source(|e| {
105//!         match e {
106//!             MyError::Foo => ExitError::new(
107//!                 "Foo",
108//!                 ExitCode::SUCCESS),
109//!             MyError::Bar => ExitError::new(
110//!                 "Bar",
111//!                 ExitCode::FAILURE),
112//!         }
113//!     })?;
114//!
115//!     Ok(())
116//! }
117//! ```
118//!
119//! Note: under the hood [try_map_on_source](explicit_error::ResultError::try_map_on_source) perform some downcasting.
120mod domain;
121mod error;
122
123pub use domain::*;
124pub use error::*;
125
126pub type Error = explicit_error::Error<DomainError>;
127pub type Result<T> = std::result::Result<T, Error>;
128
129/// Re-import from [explicit_error] crate.
130pub use explicit_error::Bug;
131
132pub mod prelude {
133    pub use crate::ResultDomainWithContext;
134    pub use explicit_error::prelude::*;
135}
136
137pub mod derive {
138    pub use explicit_error_derive::ExitError;
139}