1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! Built on top of [`explicit-error`](https://crates.io/crates/explicit-error), it provides idiomatic tools to manage errors that generate an HTTP response.
//! Based on the [explicit-error](explicit_error) crate, its chore tenet is to favor explicitness by inlining the error output while remaining concise.
//!
//! The key features are:
//! - Explicitly mark any error wrapped in a [Result] as a [Fault]. A backtrace is captured and a 500 Internal Server HTTP response generated.
//! - A derive macro [HttpError](derive::HttpError) to easily declare how enum or struct errors transform into an [Error], i.e. defines the generated HTTP response.
//! - Inline transformation of any errors wrapped in a [Result] into an [Error].
//! - Add context to errors to help debug.
//! - Monitor errors before they are transformed into proper HTTP responses.
//!
//! # A tour of explicit-error-http
//!
//! The cornerstone of the library is the [Error] type. Use `Result<T, explicit_error_http::Error>`, or equivalently `explicit_error_http::Result<T>`, as the return type of any faillible function returning errors that convert to an HTTP response.
//! Usually, it is mostly functions either called by handlers or middlewares.
//!
//! ## Inline
//!
//! In the body of the function you can explicitly turn errors into HTTP response using [HttpError] or marking them as [Fault].
//!
//! ```rust
//! use http::StatusCode;
//! use problem_details::ProblemDetails;
//! use http::Uri;
//! use explicit_error_http::{prelude::*, HttpError, Result, Fault};
//! // Import the prelude to enable functions on std::result::Result
//!
//! fn business_logic() -> Result<()> {
//! Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))
//! .or_fault()?;
//!
//! // Same behavior as fault() but the error is not captured as a source because it does not implement `[std::error::Error]`
//! Err("error message").or_fault_no_source()?;
//!
//! if 1 > 2 {
//! Err(Fault::new()
//! .with_context("Usefull context to help debug."))?;
//! }
//!
//! Err(42).map_err(|_|
//! HttpError::new(
//! StatusCode::BAD_REQUEST,
//! ProblemDetails::new()
//! .with_type(Uri::from_static("/errors/business-logic"))
//! .with_title("Informative feedback for the user."),
//! )
//! )?;
//!
//! Ok(())
//! }
//!```
//!
//! Note: The crate [problem_details] is used as an example for the HTTP response body. You can, of course, use whatever you would like that implements [Serialize](serde::Serialize).
//!
//! ## Enum and struct
//!
//! Domain errors are often represented as enum or struct as they are raised in different places.
//! To easily enable the conversion to [Error] use the [HttpError](derive::HttpError) derive and implement `From<&MyError> for HttpError`.
//!
//! ```rust
//! use http::StatusCode;
//! use problem_details::ProblemDetails;
//! use http::Uri;
//! use explicit_error_http::{prelude::*, Result, derive::HttpError, HttpError};
//!
//! #[derive(HttpError, Debug)]
//! enum MyError {
//! Foo,
//! }
//!
//! impl From<&MyError> for HttpError {
//! fn from(value: &MyError) -> Self {
//! match value {
//! MyError::Foo => HttpError::new(
//! StatusCode::BAD_REQUEST,
//! ProblemDetails::new()
//! .with_type(Uri::from_static("/errors/my-domain/foo"))
//! .with_title("Foo format incorrect.")
//! ),
//! }
//! }
//! }
//!
//! fn business_logic() -> Result<()> {
//! Err(MyError::Foo)?;
//!
//! Ok(())
//! }
//! ```
//!
//! Note: The [HttpError](derive::HttpError) derive implements the conversion to [Error], the impl of [Display](std::fmt::Display) (json format) and [std::error::Error].
//!
//! # Pattern matching
//!
//! 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.
//! 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_http::Result<T>`.
//!
//! ```rust
//! # use http::StatusCode;
//! # use http::Uri;
//! # use problem_details::ProblemDetails;
//! # use explicit_error_http::{prelude::*, HttpError, Result, derive::HttpError};
//! #[derive(HttpError, Debug)]
//! enum MyError {
//! Foo,
//! Bar,
//! }
//!
//! # impl From<&MyError> for HttpError {
//! # fn from(value: &MyError) -> Self {
//! # match value {
//! # MyError::Foo | MyError::Bar => HttpError::new(
//! # StatusCode::BAD_REQUEST,
//! # ProblemDetails::new()
//! # .with_type(Uri::from_static("/errors/my-domain/foo"))
//! # .with_title("Foo format incorrect.")
//! # ),
//! # }
//! # }
//! # }
//!
//! fn handler() -> Result<()> {
//! let err: Result<()> = Err(MyError::Foo)?;
//!
//! // Do the map if the source's type of the Error is MyError
//! err.try_map_on_source(|e| {
//! match e {
//! MyError::Foo => HttpError::new(
//! StatusCode::FORBIDDEN,
//! ProblemDetails::new()
//! .with_type(Uri::from_static("/errors/forbidden"))
//! ),
//! MyError::Bar => HttpError::new(
//! StatusCode::UNAUTHORIZED,
//! ProblemDetails::new()
//! .with_type(Uri::from_static("/errors/unauthorized"))
//! ),
//! }
//! })?;
//!
//! Ok(())
//! }
//! ```
//!
//! Note: under the hood [try_map_on_source](explicit_error::ResultError::try_map_on_source) perform some downcasting.
//!
//! ## Web frameworks
//!
//! explicit-error-http integrates well with most popular web frameworks by providing a feature flag for each of them.
//!
//! The type [Error] cannot directly be used as handlers or middlewares returned [Err] variant. A dedicated type is required.
//! The easiest implementation is to declare a [Newtype](https://doc.rust-lang.org/rust-by-example/generics/new_types.html),
//! derive it with the [HandlerError] and implement the [HandlerError] trait.
//!
//! ```rust
//! # use actix_web::{App, HttpResponse, HttpServer, get};
//! # use env_logger::Env;
//! # use explicit_error_http::{Fault, Error, HandlerError, derive::HandlerErrorHelpers};
//! # use log::{debug, error};
//! # use problem_details::ProblemDetails;
//! # use serde::Serialize;
//! #[derive(HandlerErrorHelpers)]
//! struct MyHandlerError(Error);
//!
//! impl HandlerError for MyHandlerError {
//! // Used by the derive for conversion
//! fn from_error(value: Error) -> Self {
//! MyHandlerError(value)
//! }
//!
//! // Set-up monitoring and your custom HTTP response body for faults
//! fn public_fault_response(fault: &Fault) -> impl Serialize {
//! #[cfg(debug_assertions)]
//! error!("{fault}");
//!
//! #[cfg(not(debug_assertions))]
//! error!("{}", serde_json::json!(faul));
//!
//! ProblemDetails::new()
//! .with_type(http::Uri::from_static("/errors/internal-server-error"))
//! .with_title("Internal server error")
//! }
//!
//! fn error(&self) -> &Error {
//! &self.0
//! }
//!
//! // Monitor domain variant of your errors and eventually override their body
//! fn domain_response(error: &explicit_error_http::DomainError) -> impl Serialize {
//! if error.output.http_status_code.as_u16() < 500 {
//! debug!("{error}");
//! } else {
//! error!("{error}");
//! }
//! error
//! }
//! }
//!
//! async fn my_handler() -> Result<HttpResponse, MyHandlerError> {
//! Ok(HttpResponse::Ok().finish())
//! }
//! ```
pub use *;
pub use *;
pub use *;
/// Re-import from [explicit_error] crate.
pub use Fault;
pub type Error = Error;
pub type Result<T> = Result;