1#![deny(clippy::pedantic)]
3#![allow(
4 clippy::must_use_candidate,
5 clippy::missing_errors_doc,
6 clippy::missing_panics_doc
7)]
8use std::{error::Error as StdError, fmt};
9
10use ntex_bytes::Bytes;
11
12mod bt;
13mod chain;
14mod error;
15mod ext;
16mod info;
17mod message;
18mod repr;
19pub mod utils;
20
21pub use crate::bt::{Backtrace, BacktraceResolver};
22pub use crate::chain::ErrorChain;
23pub use crate::error::Error;
24pub use crate::info::ErrorInfo;
25pub use crate::message::{ErrorMessage, ErrorMessageChained};
26pub use crate::message::{fmt_diag, fmt_diag_string, fmt_err, fmt_err_string};
27pub use crate::utils::{Success, with_service};
28
29#[doc(hidden)]
30pub use crate::bt::{set_backtrace_start, set_backtrace_start_alt};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum ResultType {
35 Success,
36 ClientError,
37 ServiceError,
38}
39
40impl ResultType {
41 pub const fn as_str(&self) -> &'static str {
43 match self {
44 ResultType::Success => "Success",
45 ResultType::ClientError => "ClientError",
46 ResultType::ServiceError => "ServiceError",
47 }
48 }
49}
50
51pub trait ResultKind: 'static {
53 fn tp(&self) -> ResultType;
55
56 fn signature(&self) -> &'static str;
60}
61
62impl ResultKind for ResultType {
63 fn tp(&self) -> ResultType {
64 *self
65 }
66
67 fn signature(&self) -> &'static str {
68 self.as_str()
69 }
70}
71
72pub trait ErrorDiagnostic: StdError + 'static {
76 type Kind: ResultKind;
77
78 fn kind(&self) -> Self::Kind;
80
81 fn tag(&self) -> Option<&Bytes> {
86 None
87 }
88
89 fn service(&self) -> Option<&'static str> {
93 None
94 }
95
96 fn backtrace(&self) -> Option<&Backtrace> {
98 None
99 }
100
101 #[track_caller]
102 fn chain(self) -> ErrorChain<Self::Kind>
103 where
104 Self: Sized,
105 {
106 ErrorChain::new(self)
107 }
108}
109
110pub trait ErrorMapping<T, E, U> {
112 fn into_error(self) -> Result<T, Error<U>>;
114}
115
116impl fmt::Display for ResultType {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "{}", self.as_str())
119 }
120}
121
122impl<T, E> ResultKind for Result<T, E>
123where
124 T: 'static,
125 E: ErrorDiagnostic + 'static,
126{
127 fn tp(&self) -> ResultType {
128 match self {
129 Ok(_) => ResultType::Success,
130 Err(e) => e.kind().tp(),
131 }
132 }
133
134 fn signature(&self) -> &'static str {
135 match self {
136 Ok(_) => ResultType::Success.as_str(),
137 Err(e) => e.kind().signature(),
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use std::{error::Error as StdError, io, mem};
145
146 use super::*;
147
148 #[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
149 enum TestKind {
150 #[error("Connect")]
151 Connect,
152 #[error("Disconnect")]
153 Disconnect,
154 #[error("ServiceError")]
155 ServiceError,
156 }
157
158 impl ResultKind for TestKind {
159 fn tp(&self) -> ResultType {
160 match self {
161 TestKind::Connect | TestKind::Disconnect => ResultType::ClientError,
162 TestKind::ServiceError => ResultType::ServiceError,
163 }
164 }
165
166 fn signature(&self) -> &'static str {
167 match self {
168 TestKind::Connect => "Client-Connect",
169 TestKind::Disconnect => "Client-Disconnect",
170 TestKind::ServiceError => "Service-Internal",
171 }
172 }
173 }
174
175 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
176 enum TestError {
177 #[error("Connect err: {0}")]
178 Connect(&'static str),
179 #[error("Disconnect")]
180 Disconnect,
181 #[error("InternalServiceError")]
182 Service(&'static str),
183 }
184
185 impl ErrorDiagnostic for TestError {
186 type Kind = TestKind;
187
188 fn kind(&self) -> Self::Kind {
189 match self {
190 TestError::Connect(_) => TestKind::Connect,
191 TestError::Disconnect => TestKind::Disconnect,
192 TestError::Service(_) => TestKind::ServiceError,
193 }
194 }
195
196 fn service(&self) -> Option<&'static str> {
197 Some("test")
198 }
199 }
200
201 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
202 #[error("TestError2")]
203 struct TestError2;
204 impl ErrorDiagnostic for TestError2 {
205 type Kind = ResultType;
206
207 fn kind(&self) -> Self::Kind {
208 ResultType::ClientError
209 }
210 }
211
212 impl From<TestError> for TestError2 {
213 fn from(_err: TestError) -> TestError2 {
214 TestError2
215 }
216 }
217
218 #[ntex::test]
219 async fn test_error() {
220 let err: Error<TestError> = TestError::Service("409 Error").into();
221 let err = err.clone();
222 assert_eq!(err.kind(), TestKind::ServiceError);
223 assert_eq!((*err).kind(), TestKind::ServiceError);
224 assert_eq!(err.to_string(), "InternalServiceError");
225 assert_eq!(err.service(), Some("test"));
226 assert_eq!(err.kind().signature(), "Service-Internal");
227 assert_eq!(
228 err,
229 Into::<Error<TestError>>::into(TestError::Service("409 Error"))
230 );
231 assert!(err.backtrace().is_some());
232
233 let err = err.set_service("SVC");
234 assert_eq!(err.service(), Some("SVC"));
235 let err = err.set_tag("TAG");
236 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
237
238 let err2: Error<TestError> = Error::new(TestError::Service("409 Error"), "TEST");
239 assert!(err != err2);
240 assert_eq!(err, TestError::Service("409 Error"));
241
242 let err2 = err2.set_tag("TAG");
243 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
244 let err2 = err2.set_service("SVC");
245 assert_eq!(err, err2);
246 let err2 = err2.map(|_| TestError::Disconnect);
247 assert!(err != err2);
248 let err2 = err2.forward(|_| TestError::Disconnect);
249 assert!(err != err2);
250
251 assert_eq!(TestError::Connect("").kind().tp(), ResultType::ClientError);
252 assert_eq!(TestError::Disconnect.kind().tp(), ResultType::ClientError);
253 assert_eq!(TestError::Service("").kind().tp(), ResultType::ServiceError);
254 assert_eq!(TestError::Connect("").to_string(), "Connect err: ");
255 assert_eq!(TestError::Disconnect.to_string(), "Disconnect");
256 assert_eq!(TestError::Disconnect.service(), Some("test"));
257 assert!(TestError::Disconnect.backtrace().is_none());
258
259 assert_eq!(ResultType::ClientError.as_str(), "ClientError");
260 assert_eq!(ResultType::ServiceError.as_str(), "ServiceError");
261 assert_eq!(ResultType::ClientError.tp(), ResultType::ClientError);
262 assert_eq!(ResultType::ServiceError.tp(), ResultType::ServiceError);
263 assert_eq!(ResultType::ClientError.to_string(), "ClientError");
264 assert_eq!(ResultType::ServiceError.to_string(), "ServiceError");
265 assert_eq!(format!("{}", ResultType::ClientError), "ClientError");
266
267 assert_eq!(TestKind::Connect.to_string(), "Connect");
268 assert_eq!(TestError::Connect("").kind().signature(), "Client-Connect");
269 assert_eq!(TestKind::Disconnect.to_string(), "Disconnect");
270 assert_eq!(
271 TestError::Disconnect.kind().signature(),
272 "Client-Disconnect"
273 );
274 assert_eq!(TestKind::ServiceError.to_string(), "ServiceError");
275 assert_eq!(
276 TestError::Service("").kind().signature(),
277 "Service-Internal"
278 );
279
280 let err = err.into_error().chain();
281 assert_eq!(err.kind(), TestKind::ServiceError);
282 assert_eq!(err.kind(), TestError::Service("409 Error").kind());
283 assert_eq!(err.to_string(), "InternalServiceError");
284 assert!(format!("{err:?}").contains("Service(\"409 Error\")"));
285 assert!(
286 format!("{:?}", err.source()).contains("Service(\"409 Error\")"),
287 "{:?}",
288 err.source().unwrap()
289 );
290
291 let err: Error<TestError> = TestError::Service("404 Error").into();
292 #[cfg(unix)]
293 {
294 if let Some(bt) = err.backtrace() {
295 bt.resolver().resolve();
296 assert!(
297 format!("{bt}").contains("ntex_error::tests::test_error"),
298 "{bt}",
299 );
300 assert!(
301 bt.repr().unwrap().contains("ntex_error::tests::test_error"),
302 "{bt}"
303 );
304 }
305 }
306
307 let err: ErrorChain<TestKind> = err.into();
308 assert_eq!(err.kind(), TestKind::ServiceError);
309 assert_eq!(err.kind(), TestError::Service("404 Error").kind());
310 assert_eq!(err.service(), Some("test"));
311 assert_eq!(err.kind().signature(), "Service-Internal");
312 assert_eq!(err.to_string(), "InternalServiceError");
313 assert!(err.backtrace().is_some());
314 assert!(format!("{err:?}").contains("Service(\"404 Error\")"));
315
316 assert_eq!(24, mem::size_of::<TestError>());
317 assert_eq!(8, mem::size_of::<Error<TestError>>());
318
319 assert_eq!(TestError2.service(), None);
320 assert_eq!(TestError2.kind().signature(), "ClientError");
321
322 let err: Error<TestError> = TestError::Service("409 Error").into();
324 let msg = fmt_err_string(&err);
325 assert_eq!(msg, "InternalServiceError\n");
326 let msg = fmt_diag_string(&err);
327 assert!(msg.contains("err: InternalServiceError"));
328
329 let err: ErrorInfo = err.set_service("SVC").into();
330 assert_eq!(err.tp(), ResultType::ServiceError);
331 assert_eq!(err.service(), Some("SVC"));
332 assert_eq!(err.signature(), "Service-Internal");
333 assert!(err.backtrace().is_some());
334
335 let res = Err(TestError::Service("409 Error"));
336 let res: Result<(), Error<TestError>> = res.into_error();
337 let _res: Result<(), Error<TestError2>> = res.into_error();
338
339 let msg = fmt_err_string(&err);
340 assert_eq!(msg, "InternalServiceError\n");
341 let msg = fmt_diag_string(&err);
342 assert!(msg.contains("err: InternalServiceError"));
343
344 let err: Error<TestError> = TestError::Service("409 Error").into();
346 assert_eq!(err.get_item::<&str>(), None);
347 let err = err.insert_item("Test");
348 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
349 let err2 = err.clone();
350 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
351 let err2 = err2.insert_item("Test2");
352 assert_eq!(err2.get_item::<&str>(), Some(&"Test2"));
353 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
354 let err2 = err.clone().map(|_| TestError::Disconnect);
355 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
356
357 let info = ErrorInfo::from(&err2);
358 assert_eq!(info.get_item::<&str>(), Some(&"Test"));
359
360 let err3 = err
361 .clone()
362 .try_map(|_| Err::<(), _>(TestError2))
363 .err()
364 .unwrap();
365 assert_eq!(err3.kind().signature(), "ClientError");
366 assert_eq!(err3.get_item::<&str>(), Some(&"Test"));
367
368 let res = err.clone().try_map(|_| Ok::<_, TestError2>(()));
369 assert_eq!(res, Ok(()));
370
371 assert_eq!(Success.kind(), ResultType::Success);
372 assert_eq!(format!("{Success}"), "Success");
373
374 assert_eq!(
375 ErrorDiagnostic::kind(&io::Error::other("")),
376 ResultType::ServiceError
377 );
378 assert_eq!(
379 ErrorDiagnostic::kind(&io::Error::new(io::ErrorKind::InvalidData, "")),
380 ResultType::ClientError
381 );
382
383 let res = Ok::<_, TestError>(());
384 assert_eq!(res.tp(), ResultType::Success);
385
386 let res = Err::<(), _>(TestError::Service("409 Error"));
387 assert_eq!(res.tp(), ResultType::ServiceError);
388 assert_eq!(res.signature(), "Service-Internal");
389 }
390}