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