1use std::{error, fmt};
2
3pub use crate::value::{InvalidHeaderValue, ToStrError};
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash)]
6pub struct Error {
13 inner: ErrorKind,
14}
15
16#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[error("Invalid URI")]
18pub struct InvalidUri {
20 _priv: (),
21}
22
23#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[error("Invalid HTTP header name")]
25pub struct InvalidHeaderName {
27 _priv: (),
28}
29
30#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[error("Invalid status code")]
32pub struct InvalidStatusCode {
37 _priv: (),
38}
39
40#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[error("Invalid HTTP method")]
42pub struct InvalidMethod {
44 _priv: (),
45}
46
47#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
48enum ErrorKind {
49 StatusCode(InvalidStatusCode),
50 Method(InvalidMethod),
51 Uri(InvalidUri),
52 UriParts(InvalidUri),
53 HeaderName(InvalidHeaderName),
54 HeaderValue(InvalidHeaderValue),
55 Http,
56}
57
58impl fmt::Debug for Error {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.debug_tuple("ntex_http::Error")
61 .field(&self.get_ref())
63 .finish()
64 }
65}
66
67impl fmt::Display for Error {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 fmt::Display::fmt(self.get_ref(), f)
70 }
71}
72
73impl Error {
74 pub fn is<T: error::Error + 'static>(&self) -> bool {
76 self.get_ref().is::<T>()
77 }
78
79 pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
81 match self.inner {
82 ErrorKind::StatusCode(ref e) => e,
83 ErrorKind::Method(ref e) => e,
84 ErrorKind::Uri(ref e) | ErrorKind::UriParts(ref e) => e,
85 ErrorKind::HeaderName(ref e) => e,
86 ErrorKind::HeaderValue(ref e) => e,
87 ErrorKind::Http => &DEFAULT_ERR,
88 }
89 }
90}
91
92#[derive(thiserror::Error, Copy, Clone, Debug)]
93#[error("{_0}")]
94struct ErrorMessage(&'static str);
95
96const DEFAULT_ERR: ErrorMessage = ErrorMessage("http error");
97
98impl error::Error for Error {
99 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
102 self.get_ref().source()
103 }
104}
105
106impl From<http::status::InvalidStatusCode> for Error {
107 fn from(_: http::status::InvalidStatusCode) -> Error {
108 Error {
109 inner: ErrorKind::StatusCode(InvalidStatusCode { _priv: () }),
110 }
111 }
112}
113
114impl From<http::method::InvalidMethod> for Error {
115 fn from(_: http::method::InvalidMethod) -> Error {
116 Error {
117 inner: ErrorKind::Method(InvalidMethod { _priv: () }),
118 }
119 }
120}
121
122impl From<http::uri::InvalidUri> for Error {
123 fn from(_: http::uri::InvalidUri) -> Error {
124 Error {
125 inner: ErrorKind::Uri(InvalidUri { _priv: () }),
126 }
127 }
128}
129
130impl From<http::uri::InvalidUriParts> for Error {
131 fn from(_: http::uri::InvalidUriParts) -> Error {
132 Error {
133 inner: ErrorKind::UriParts(InvalidUri { _priv: () }),
134 }
135 }
136}
137
138impl From<http::header::InvalidHeaderName> for Error {
139 fn from(_: http::header::InvalidHeaderName) -> Error {
140 Error {
141 inner: ErrorKind::HeaderName(InvalidHeaderName { _priv: () }),
142 }
143 }
144}
145
146impl From<InvalidHeaderValue> for Error {
147 fn from(err: InvalidHeaderValue) -> Error {
148 Error {
149 inner: ErrorKind::HeaderValue(err),
150 }
151 }
152}
153
154impl From<http::Error> for Error {
155 fn from(err: http::Error) -> Error {
156 let inner = if err.is::<http::status::InvalidStatusCode>() {
157 ErrorKind::StatusCode(InvalidStatusCode { _priv: () })
158 } else if err.is::<http::method::InvalidMethod>() {
159 ErrorKind::Method(InvalidMethod { _priv: () })
160 } else if err.is::<http::uri::InvalidUri>() {
161 ErrorKind::Uri(InvalidUri { _priv: () })
162 } else if err.is::<http::header::InvalidHeaderName>() {
163 ErrorKind::HeaderName(InvalidHeaderName { _priv: () })
164 } else if err.is::<http::header::InvalidHeaderValue>() {
165 ErrorKind::HeaderValue(InvalidHeaderValue::default())
166 } else {
167 ErrorKind::Http
168 };
169 Error { inner }
170 }
171}
172
173impl From<std::convert::Infallible> for Error {
174 fn from(err: std::convert::Infallible) -> Error {
175 match err {}
176 }
177}
178
179impl From<http::status::InvalidStatusCode> for InvalidStatusCode {
180 fn from(_: http::status::InvalidStatusCode) -> InvalidStatusCode {
181 InvalidStatusCode { _priv: () }
182 }
183}
184
185impl From<http::method::InvalidMethod> for InvalidMethod {
186 fn from(_: http::method::InvalidMethod) -> InvalidMethod {
187 InvalidMethod { _priv: () }
188 }
189}
190
191impl From<http::uri::InvalidUri> for InvalidUri {
192 fn from(_: http::uri::InvalidUri) -> InvalidUri {
193 InvalidUri { _priv: () }
194 }
195}
196
197impl From<http::header::InvalidHeaderName> for InvalidHeaderName {
198 fn from(_: http::header::InvalidHeaderName) -> InvalidHeaderName {
199 InvalidHeaderName { _priv: () }
200 }
201}
202
203impl From<http::header::InvalidHeaderValue> for InvalidHeaderValue {
204 fn from(_: http::header::InvalidHeaderValue) -> InvalidHeaderValue {
205 InvalidHeaderValue::default()
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use std::error::Error as StdError;
213
214 #[test]
215 fn inner_http_error() {
216 let e = http::method::Method::from_bytes(b"").unwrap_err();
217 let err: Error = http::Error::from(e).into();
218 let ie = err.get_ref();
219 assert!(!ie.is::<http::Error>());
220 }
221
222 #[test]
223 fn inner_error_is_invalid_status_code() {
224 let e = http::status::StatusCode::from_u16(6666).unwrap_err();
225 let err: Error = e.into();
226 let ie = err.get_ref();
227 assert!(!ie.is::<InvalidHeaderValue>());
228 assert!(ie.is::<InvalidStatusCode>());
229 ie.downcast_ref::<InvalidStatusCode>().unwrap();
230
231 assert!(err.source().is_none());
232 assert!(!err.is::<InvalidHeaderValue>());
233 assert!(err.is::<InvalidStatusCode>());
234
235 let s = format!("{err:?}");
236 assert!(s.starts_with("ntex_http::Error"));
237 }
238
239 #[test]
240 fn inner_error_is_invalid_method() {
241 let e = http::method::Method::from_bytes(b"").unwrap_err();
242 let err: Error = e.into();
243 let ie = err.get_ref();
244 assert!(ie.is::<InvalidMethod>());
245 ie.downcast_ref::<InvalidMethod>().unwrap();
246
247 assert!(err.source().is_none());
248 assert!(err.is::<InvalidMethod>());
249
250 let s = format!("{err:?}");
251 assert!(s.starts_with("ntex_http::Error"));
252 }
253}