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
// TODO: Check Clippy lints
// TODO: Document each test module for tests
// TODO: Add security mention in readme.md
use TokenStream;
use parse_macro_input;
use crateErrorResponse;
use crate;
use crateerror_response_output;
use crateproof_route_output;
/// # `ErrorResponse` Derive Macro
///
/// This macro is a helper to implement `Into<actix_web::HttpResponse>`
/// and `Into<actix_web::Error>` for `thiserror::error` marked enumerables.
///
/// Have in mind that nothing really enforces that the enum you apply this on
/// has also derived `thiserror::error`, but this macro's generation will rely
/// on you having implemented `Display`, and `thiserror::error` is a convenient
/// way to implement `Display`.
///
/// ## Macro attributes
///
/// With `ErrorResponse` you can modify how your your response will look when
/// returning an error from an endpoint.
///
/// **`#[transform_response(function_reference)]`**
/// You can add this attribute to your enum and pass a static function reference
/// the function should receive an `HttpResponseBuilder` which is the partially
/// built response and a `String` as second argument, which is the result of
/// `<Self as Display>::to_string()` where `Self` is the enum you applied this
/// function to. The function should return an `HttpResponse` which is what's
/// going to be used when an error is returned from an endpoint.
///
/// **`#[default_status_code(number_or_identifier)]`**
/// You can add this attribute to your enum and pass or either a number
/// representing the http error status code like `400` or `500`, or an
/// identifier such as `BadRequest` or `InternalServerError`. This will set the
/// status code by default if you don't use `status_code` in any enum variant.
///
/// **`#[status_code(number_or_identifier)]`**
/// Like `default_status_code` you can pass a number or an HTTP status code
/// identifier and it will be applied to the current enum variant.
///
/// By default all status codes will be `InternalServerError` and the enum's
/// Display will be applied to the response body.
///
/// ## Example
///
/// ```rust
/// use actix_failwrap::ErrorResponse;
/// use thiserror::Error;
/// use serde_json::json;
/// use chrono::Utc;
/// use actix_web::{HttpResponse, HttpResponseBuilder};
///
/// fn transformer(mut builder: HttpResponseBuilder, display: String) -> HttpResponse {
/// builder
/// .body(json! {
/// {
/// "error": display,
/// "date": Utc::now()
/// .to_rfc3339()
/// .to_string()
/// }
/// }.to_string())
/// }
///
/// #[derive(ErrorResponse, Error, Debug)]
/// #[transform_response(transformer)]
/// #[default_status_code(InternalServerError)] // this is already by default
/// enum CustomError {
/// #[error("This password is already in use by {email}, please chose another.")]
/// #[status_code(BadRequest)]
/// PasswordAlreadyInUse { email: String } // :)
/// }
///
/// // This can then be used with the `proof_route` macro.
/// ```
/// # `proof_route` Attribute Macro
///
/// You can replace the `actix_web::{get, post, put, ..}` macros by
/// `proof_route` with the following syntax `#[proof_route("METHOD /path")]`
/// resembling to the HTTP standard syntax.
///
/// **Before using this macro see [`ErrorResponse`] as you need it to use this**
///
/// This macro creates a new `actix_web` route, the syntax is the same as normal
/// attribute marked routes, except the return type changes to be a `Result<T,
/// E>` where `T` should implement `::actix_web::Responder` and `E` should
/// implement `Into<::actix_web::HttpResponse>` which you can implement in your
/// response type by using [`ErrorResponse`].
///
/// Since this thightly integrates with with `thiserror` you can join multiple
/// error types and use `?` to make your error handling in routes ergonomic.
///
/// If you return a custom error not annotated with [`ErrorResponse`] this is
/// considered undefined behavior, and no support will be given to that.
///
/// ## Macro Attributes
///
/// **`#[error_override(EnumVariant)]`**
///
/// You can also annotate your route extractors with the `error_override`
/// attribute which expects a variant of the error enumerable being returned.
/// This will replace any error that may be returned by the collector itself for
/// a custom error variant instead.
///
/// ## Example
///
/// ```rust
/// use actix_failwrap::{ErrorResponse, proof_route};
/// use thiserror::Error;
/// use actix_web::web::Json;
/// use actix_web::HttpResponse;
/// use serde::Deserialize;
///
/// // we should declare an example error enum
///
/// #[derive(ErrorResponse, Error, Debug)]
/// enum CustomError {
/// #[error("An invalid body was received.")]
/// InvalidBody,
///
/// #[error("Something went wrong.")]
/// SomeError // by default this is an InternalServerError.
/// }
///
/// #[derive(Deserialize)]
/// struct CreateAccountData {
/// email: String,
/// passowrd: String
/// }
///
/// #[proof_route("POST /account")]
/// async fn create_account(
/// // in the case the user sends wrong data the error will be overriden.
/// #[error_override(InvalidBody)] body: Json<CreateAccountData>
/// ) -> Result<HttpResponse, CustomError> {
/// Err(CustomError::SomeError) // we directly return the error here
/// }
/// ```