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