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