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
201 let err = err.set_service("SVC");
202 assert_eq!(err.service(), Some("SVC"));
203
204 let err2: Error<TestError> = Error::new(TestError::Service("409 Error"), "TEST");
205 assert!(err != err2);
206 assert_eq!(err, TestError::Service("409 Error"));
207
208 let err2 = err2.set_service("SVC");
209 assert_eq!(err, err2);
210 let err2 = err2.map(|_| TestError::Disconnect);
211 assert!(err != err2);
212 let err2 = err2.forward(|_| TestError::Disconnect);
213 assert!(err != err2);
214
215 assert_eq!(TestError::Connect("").kind().tp(), ResultType::ClientError);
216 assert_eq!(TestError::Disconnect.kind().tp(), ResultType::ClientError);
217 assert_eq!(TestError::Service("").kind().tp(), ResultType::ServiceError);
218 assert_eq!(TestError::Connect("").to_string(), "Connect err: ");
219 assert_eq!(TestError::Disconnect.to_string(), "Disconnect");
220 assert_eq!(TestError::Disconnect.service(), Some("test"));
221 assert!(TestError::Disconnect.backtrace().is_none());
222
223 assert_eq!(ResultType::ClientError.as_str(), "ClientError");
224 assert_eq!(ResultType::ServiceError.as_str(), "ServiceError");
225 assert_eq!(ResultType::ClientError.tp(), ResultType::ClientError);
226 assert_eq!(ResultType::ServiceError.tp(), ResultType::ServiceError);
227 assert_eq!(ResultType::ClientError.to_string(), "ClientError");
228 assert_eq!(ResultType::ServiceError.to_string(), "ServiceError");
229 assert_eq!(format!("{}", ResultType::ClientError), "ClientError");
230
231 assert_eq!(TestKind::Connect.to_string(), "Connect");
232 assert_eq!(TestError::Connect("").kind().signature(), "Client-Connect");
233 assert_eq!(TestKind::Disconnect.to_string(), "Disconnect");
234 assert_eq!(
235 TestError::Disconnect.kind().signature(),
236 "Client-Disconnect"
237 );
238 assert_eq!(TestKind::ServiceError.to_string(), "ServiceError");
239 assert_eq!(
240 TestError::Service("").kind().signature(),
241 "Service-Internal"
242 );
243
244 let err = err.into_error().chain();
245 assert_eq!(err.kind(), TestKind::ServiceError);
246 assert_eq!(err.kind(), TestError::Service("409 Error").kind());
247 assert_eq!(err.to_string(), "InternalServiceError");
248 assert!(format!("{err:?}").contains("Service(\"409 Error\")"));
249 assert!(
250 format!("{:?}", err.source()).contains("Service(\"409 Error\")"),
251 "{:?}",
252 err.source().unwrap()
253 );
254
255 let err: Error<TestError> = TestError::Service("404 Error").into();
256 if let Some(bt) = err.backtrace() {
257 assert!(
258 format!("{bt}").contains("ntex_error::tests::test_error"),
259 "{bt}",
260 );
261 assert!(bt.repr().contains("ntex_error::tests::test_error"), "{bt}",);
262 }
263
264 let err: ErrorChain<TestKind> = err.into();
265 assert_eq!(err.kind(), TestKind::ServiceError);
266 assert_eq!(err.kind(), TestError::Service("404 Error").kind());
267 assert_eq!(err.service(), Some("test"));
268 assert_eq!(err.kind().signature(), "Service-Internal");
269 assert_eq!(err.to_string(), "InternalServiceError");
270 assert!(err.backtrace().is_some());
271 assert!(format!("{err:?}").contains("Service(\"404 Error\")"));
272
273 assert_eq!(24, mem::size_of::<TestError>());
274 assert_eq!(8, mem::size_of::<Error<TestError>>());
275
276 assert_eq!(TestError2.service(), None);
277 assert_eq!(TestError2.kind().signature(), "ClientError");
278
279 let err: Error<TestError> = TestError::Service("409 Error").into();
281 let msg = fmt_err_string(&err);
282 assert_eq!(msg, "InternalServiceError\n");
283 let msg = fmt_diag_string(&err);
284 assert!(msg.contains("err: InternalServiceError"));
285
286 let err: ErrorInfo = err.set_service("SVC").into();
287 assert_eq!(err.tp(), ResultType::ServiceError);
288 assert_eq!(err.service(), Some("SVC"));
289 assert_eq!(err.signature(), "Service-Internal");
290 assert!(err.backtrace().is_some());
291
292 let res = Err(TestError::Service("409 Error"));
293 let res: Result<(), Error<TestError>> = res.into_error();
294 let _res: Result<(), Error<TestError2>> = res.into_error();
295
296 let msg = fmt_err_string(&err);
297 assert_eq!(msg, "InternalServiceError\n");
298 let msg = fmt_diag_string(&err);
299 assert!(msg.contains("err: InternalServiceError"));
300
301 let err: Error<TestError> = TestError::Service("409 Error").into();
303 assert_eq!(err.get_item::<&str>(), None);
304 let err = err.insert_item("Test");
305 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
306 let err2 = err.clone();
307 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
308 let err2 = err2.insert_item("Test2");
309 assert_eq!(err2.get_item::<&str>(), Some(&"Test2"));
310 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
311
312 let info = ErrorInfo::from(err2);
313 assert_eq!(info.get_item::<&str>(), Some(&"Test2"));
314 }
315}