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