Skip to content

Commit b88c33f

Browse files
committed
feat: implement custom http Error module
1 parent 519df26 commit b88c33f

File tree

5 files changed

+298
-4
lines changed

5 files changed

+298
-4
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ keywords = [
2727
[dependencies]
2828
hyper = { version = "0.14", default-features = false, features = ["server", "tcp"] }
2929
anyhow = "1.0"
30+
thiserror = "1.0"
3031

3132
[dev-dependencies]
3233
hyper = { version = "0.14", features = ["tcp", "server", "http1"] }

src/error.rs

+291
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
//! Custom HTTP Error module.
2+
3+
use hyper::StatusCode;
4+
use std::fmt;
5+
use thiserror::Error as ThisError;
6+
7+
/// Represents an HTTP Error.
8+
#[derive(ThisError, Debug)]
9+
pub struct Error {
10+
source: anyhow::Error,
11+
status: Option<StatusCode>,
12+
}
13+
14+
impl Error {
15+
/// Returns the underlying error.
16+
pub fn source(self) -> anyhow::Error {
17+
self.source
18+
}
19+
20+
/// Returns an HTTP Status Code reference associated with the underlying error.
21+
pub fn status(&self) -> Option<&StatusCode> {
22+
self.status.as_ref()
23+
}
24+
25+
/// Adds/updates the current HTTP Status Code.
26+
pub fn with_status(mut self, status: StatusCode) -> Self {
27+
self.status = Some(status);
28+
self
29+
}
30+
}
31+
32+
impl fmt::Display for Error {
33+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34+
fmt::Display::fmt(&self.source, f)
35+
}
36+
}
37+
38+
impl From<hyper::Error> for Error {
39+
/// Converts a [`hyper::Error`] type into an HTTP [`Error`].
40+
fn from(source: hyper::Error) -> Self {
41+
Self {
42+
source: anyhow::anyhow!(source),
43+
status: None,
44+
}
45+
}
46+
}
47+
48+
impl From<anyhow::Error> for Error {
49+
/// Converts whatever error type that implements [`std::error::Error`] into an HTTP [`Error`].
50+
fn from(source: anyhow::Error) -> Self {
51+
Self {
52+
source,
53+
status: None,
54+
}
55+
}
56+
}
57+
58+
impl From<&str> for Error {
59+
/// Converts a string error into an HTTP [`Error`].
60+
fn from(source: &str) -> Self {
61+
Self {
62+
source: anyhow::anyhow!(source.to_owned()),
63+
status: None,
64+
}
65+
}
66+
}
67+
68+
#[macro_export]
69+
macro_rules! http_error {
70+
($($arg:tt)*) => {{
71+
Error::from_err(anyhow::anyhow!($($arg)*), None)
72+
}}
73+
}
74+
75+
// 4xx
76+
/// Construct an `Error` with `StatusCode::BAD_REQUEST` from a string or existing non-anyhow error value.
77+
#[macro_export]
78+
macro_rules! bad_request {
79+
($($arg:tt)*) => {{
80+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::BAD_REQUEST))
81+
}}
82+
}
83+
84+
/// Construct an `Error` with `StatusCode::UNAUTHORIZED` from a string or existing non-anyhow error value.
85+
#[macro_export]
86+
macro_rules! unauthorized {
87+
($($arg:tt)*) => {{
88+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::UNAUTHORIZED))
89+
}}
90+
}
91+
92+
/// Construct an `Error` with `StatusCode::PAYMENT_REQUIRED` from a string or existing non-anyhow error value.
93+
#[macro_export]
94+
macro_rules! payment_required {
95+
($($arg:tt)*) => {{
96+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::PAYMENT_REQUIRED))
97+
}}
98+
}
99+
100+
/// Construct an `Error` with `StatusCode::FORBIDDEN` from a string or existing non-anyhow error value.
101+
#[macro_export]
102+
macro_rules! forbidden {
103+
($($arg:tt)*) => {{
104+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::FORBIDDEN))
105+
}}
106+
}
107+
108+
/// Construct an `Error` with `StatusCode::NOT_FOUND` from a string or existing non-anyhow error value.
109+
#[macro_export]
110+
macro_rules! not_found {
111+
($($arg:tt)*) => {{
112+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::NOT_FOUND))
113+
}}
114+
}
115+
116+
/// Construct an `Error` with `StatusCode::METHOD_NOT_ALLOWED` from a string or existing non-anyhow error value.
117+
#[macro_export]
118+
macro_rules! method_not_allowed {
119+
($($arg:tt)*) => {{
120+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::METHOD_NOT_ALLOWED))
121+
}}
122+
}
123+
124+
/// Construct an `Error` with `StatusCode::NOT_ACCEPTABLE` from a string or existing non-anyhow error value.
125+
#[macro_export]
126+
macro_rules! not_acceptable {
127+
($($arg:tt)*) => {{
128+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::NOT_ACCEPTABLE))
129+
}}
130+
}
131+
132+
/// Construct an `Error` with `StatusCode::PROXY_AUTHENTICATION_REQUIRED` from a string or existing non-anyhow error value.
133+
#[macro_export]
134+
macro_rules! proxy_authentication_required {
135+
($($arg:tt)*) => {{
136+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::PROXY_AUTHENTICATION_REQUIRED))
137+
}}
138+
}
139+
140+
/// Construct an `Error` with `StatusCode::REQUEST_TIMEOUT` from a string or existing non-anyhow error value.
141+
#[macro_export]
142+
macro_rules! request_timeout {
143+
($($arg:tt)*) => {{
144+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::REQUEST_TIMEOUT))
145+
}}
146+
}
147+
148+
/// Construct an `Error` with `StatusCode::CONFLICT` from a string or existing non-anyhow error value.
149+
#[macro_export]
150+
macro_rules! conflict {
151+
($($arg:tt)*) => {{
152+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::CONFLICT))
153+
}}
154+
}
155+
156+
/// Construct an `Error` with `StatusCode::GONE` from a string or existing non-anyhow error value.
157+
#[macro_export]
158+
macro_rules! gone {
159+
($($arg:tt)*) => {{
160+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::GONE))
161+
}}
162+
}
163+
164+
/// Construct an `Error` with `StatusCode::LENGTH_REQUIRED` from a string or existing non-anyhow error value.
165+
#[macro_export]
166+
macro_rules! length_required {
167+
($($arg:tt)*) => {{
168+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::LENGTH_REQUIRED))
169+
}}
170+
}
171+
172+
/// Construct an `Error` with `StatusCode::PRECONDITION_FAILED` from a string or existing non-anyhow error value.
173+
#[macro_export]
174+
macro_rules! precondition_failed {
175+
($($arg:tt)*) => {{
176+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::PRECONDITION_FAILED))
177+
}}
178+
}
179+
180+
/// Construct an `Error` with `StatusCode::PAYLOAD_TOO_LARGE` from a string or existing non-anyhow error value.
181+
#[macro_export]
182+
macro_rules! payload_too_large {
183+
($($arg:tt)*) => {{
184+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::PAYLOAD_TOO_LARGE))
185+
}}
186+
}
187+
188+
/// Construct an `Error` with `StatusCode::URI_TOO_LONG` from a string or existing non-anyhow error value.
189+
#[macro_export]
190+
macro_rules! uri_too_long {
191+
($($arg:tt)*) => {{
192+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::URI_TOO_LONG))
193+
}}
194+
}
195+
196+
/// Construct an `Error` with `StatusCode::UNSUPPORTED_MEDIA_TYPE` from a string or existing non-anyhow error value.
197+
#[macro_export]
198+
macro_rules! unsupported_media_type {
199+
($($arg:tt)*) => {{
200+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::UNSUPPORTED_MEDIA_TYPE))
201+
}}
202+
}
203+
204+
/// Construct an `Error` with `StatusCode::RANGE_NOT_SATISFIABLE` from a string or existing non-anyhow error value.
205+
#[macro_export]
206+
macro_rules! range_not_satisfiable {
207+
($($arg:tt)*) => {{
208+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::RANGE_NOT_SATISFIABLE))
209+
}}
210+
}
211+
212+
/// Construct an `Error` with `StatusCode::EXPECTATION_FAILED` from a string or existing non-anyhow error value.
213+
#[macro_export]
214+
macro_rules! expectation_failed {
215+
($($arg:tt)*) => {{
216+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::EXPECTATION_FAILED))
217+
}}
218+
}
219+
220+
// 50x
221+
/// Construct an `Error` with `StatusCode::INTERNAL_SERVER_ERROR` from a string or existing non-anyhow error value.
222+
#[macro_export]
223+
macro_rules! internal_server_error {
224+
($($arg:tt)*) => {{
225+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::INTERNAL_SERVER_ERROR))
226+
}}
227+
}
228+
229+
/// Construct an `Error` with `StatusCode::NOT_IMPLEMENTED` from a string or existing non-anyhow error value.
230+
#[macro_export]
231+
macro_rules! not_implemented {
232+
($($arg:tt)*) => {{
233+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::NOT_IMPLEMENTED))
234+
}}
235+
}
236+
237+
/// Construct an `Error` with `StatusCode::BAD_GATEWAY` from a string or existing non-anyhow error value.
238+
#[macro_export]
239+
macro_rules! bad_gateway {
240+
($($arg:tt)*) => {{
241+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::BAD_GATEWAY))
242+
}}
243+
}
244+
245+
/// Construct an `Error` with `StatusCode::SERVICE_UNAVAILABLE` from a string or existing non-anyhow error value.
246+
#[macro_export]
247+
macro_rules! service_unavailable {
248+
($($arg:tt)*) => {{
249+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::SERVICE_UNAVAILABLE))
250+
}}
251+
}
252+
253+
/// Construct an `Error` with `StatusCode::GATEWAY_TIMEOUT` from a string or existing non-anyhow error value.
254+
#[macro_export]
255+
macro_rules! gateway_timeout {
256+
($($arg:tt)*) => {{
257+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::GATEWAY_TIMEOUT))
258+
}}
259+
}
260+
261+
/// Construct an `Error` with `StatusCode::HTTP_VERSION_NOT_SUPPORTED` from a string or existing non-anyhow error value.
262+
#[macro_export]
263+
macro_rules! http_version_not_supported {
264+
($($arg:tt)*) => {{
265+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::HTTP_VERSION_NOT_SUPPORTED))
266+
}}
267+
}
268+
269+
/// Construct an `Error` with `StatusCode::VARIANT_ALSO_NEGOTIATES` from a string or existing non-anyhow error value.
270+
#[macro_export]
271+
macro_rules! variant_also_negotiates {
272+
($($arg:tt)*) => {{
273+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::VARIANT_ALSO_NEGOTIATES))
274+
}}
275+
}
276+
277+
/// Construct an `Error` with `StatusCode::INSUFFICIENT_STORAGE` from a string or existing non-anyhow error value.
278+
#[macro_export]
279+
macro_rules! insufficient_storage {
280+
($($arg:tt)*) => {{
281+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::INSUFFICIENT_STORAGE))
282+
}}
283+
}
284+
285+
/// Construct an `Error` with `StatusCode::LOOP_DETECTED` from a string or existing non-anyhow error value.
286+
#[macro_export]
287+
macro_rules! loop_detected {
288+
($($arg:tt)*) => {{
289+
Error::from_err(anyhow::anyhow!($($arg)*), Some(StatusCode::LOOP_DETECTED))
290+
}}
291+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
//! - Convenient [`Error`] and [`Result`] types powered by [anyhow](https://github.com/dtolnay/anyhow).
1313
//!
1414
15+
pub mod error;
1516
pub mod middleware;
1617
pub mod remote_addr;
1718
pub mod service;
1819
pub mod types;
1920

21+
pub use error::*;
2022
pub use middleware::*;
2123
pub use remote_addr::*;
2224
pub use service::*;

src/service.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@ mod handler_service {
8888
use std::sync::Arc;
8989
use std::task::{Context, Poll};
9090

91+
use crate::error::Error;
9192
use crate::middleware::Handler;
9293
use crate::service::HyperService;
93-
use crate::types::{Error, Request, Response, Result};
94+
use crate::types::{Request, Response, Result};
9495

9596
pub struct HandlerService<H> {
9697
handler: Arc<H>,

src/types.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Set of types aliases for convenience.
22
3+
use crate::Error;
4+
35
/// A [`hyper::Body`] type alias.
46
pub type Body = hyper::Body;
57

@@ -9,8 +11,5 @@ pub type Request<T = Body> = hyper::Request<T>;
911
/// A [`hyper::Response<Body>`] type alias.
1012
pub type Response<T = Body> = hyper::Response<T>;
1113

12-
/// An [`anyhow::Error`] type alias.
13-
pub type Error = anyhow::Error;
14-
1514
/// An [`anyhow::Result`] type alias.
1615
pub type Result<T = (), E = Error> = anyhow::Result<T, E>;

0 commit comments

Comments
 (0)