1#![deny(clippy::pedantic)]
11#![allow(
12 clippy::must_use_candidate,
13 clippy::missing_errors_doc,
14 clippy::missing_panics_doc
15)]
16use std::{error::Error as StdError, fmt};
17
18use ntex_bytes::Bytes;
19
20mod bt;
21mod error;
22mod ext;
23mod info;
24mod message;
25mod repr;
26pub mod utils;
28
29pub use crate::bt::{Backtrace, BacktraceRaw, BacktraceResolver};
30pub use crate::error::Error;
31pub use crate::info::{Failure, FailureDiagnostic};
32pub use crate::message::{ErrorMessage, ErrorMessageChained};
33pub use crate::message::{fmt_diag, fmt_diag_string, fmt_diag_typ, fmt_err, fmt_err_string};
34pub use crate::utils::{ResultSignature, Retryable, Success, with_service};
35
36#[doc(hidden)]
37pub use crate::bt::{set_backtrace_start, set_backtrace_start_alt};
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum ResultType {
42 Success,
44 ClientError,
46 ServiceError,
48}
49
50impl ResultType {
51 pub const fn as_str(self) -> &'static str {
53 match self {
54 ResultType::Success => "Success",
55 ResultType::ClientError => "ClientError",
56 ResultType::ServiceError => "ServiceError",
57 }
58 }
59}
60
61pub trait AsError {
63 type Target: ErrorDiagnostic;
65
66 fn as_diag(&self) -> &Self::Target;
68}
69
70pub trait ErrorDiagnostic: StdError + 'static {
74 fn signature(&self) -> &'static str;
78
79 fn tag(&self) -> Option<&Bytes> {
84 None
85 }
86
87 fn service(&self) -> Option<&'static str> {
91 None
92 }
93
94 fn backtrace(&self) -> Option<&Backtrace> {
96 None
97 }
98
99 #[doc(hidden)]
100 #[track_caller]
101 fn into_failure(self) -> Failure
105 where
106 Self: Sized,
107 {
108 Failure::from(Error::from(self))
109 }
110}
111
112pub trait ErrorMapping<T, E, U> {
114 fn into_error(self) -> Result<T, Error<U>>;
116}
117
118impl fmt::Display for ResultType {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "{}", self.as_str())
121 }
122}
123
124pub trait IntoFailure: Sized {
126 fn fail(self) -> Failure;
128}
129
130#[cfg(test)]
131mod tests {
132 use std::{error::Error as StdError, mem};
133
134 use super::*;
135
136 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
137 enum TestError {
138 #[error("Connect err: {0}")]
139 Connect(&'static str),
140 #[error("Disconnect")]
141 Disconnect,
142 #[error("InternalServiceError")]
143 Service(&'static str),
144 }
145
146 impl ErrorDiagnostic for TestError {
147 fn signature(&self) -> &'static str {
148 match self {
149 TestError::Connect(_) => "Client-Connect",
150 TestError::Disconnect => "Client-Disconnect",
151 TestError::Service(_) => "Service-Internal",
152 }
153 }
154
155 fn service(&self) -> Option<&'static str> {
156 Some("test")
157 }
158 }
159
160 impl From<&TestError> for ResultType {
161 fn from(err: &TestError) -> ResultType {
162 match err {
163 TestError::Connect(_) | TestError::Disconnect => ResultType::ClientError,
164 TestError::Service(_) => ResultType::ServiceError,
165 }
166 }
167 }
168
169 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
170 #[error("TestError2")]
171 struct TestError2;
172 impl ErrorDiagnostic for TestError2 {
173 fn signature(&self) -> &'static str {
174 "TestError2"
175 }
176 }
177
178 impl From<TestError> for TestError2 {
179 fn from(_err: TestError) -> TestError2 {
180 TestError2
181 }
182 }
183
184 #[ntex::test]
185 async fn test_error() {
186 let err: Error<TestError> = TestError::Service("409 Error").into();
187 let err = err.clone();
188 assert_eq!(err.to_string(), "InternalServiceError");
189 assert_eq!(err.service(), Some("test"));
190 assert_eq!(err.signature(), "Service-Internal");
191 assert_eq!(
192 err,
193 Into::<Error<TestError>>::into(TestError::Service("409 Error"))
194 );
195 assert!(err.backtrace().is_some());
196
197 let err = err.with_service("SVC");
198 assert_eq!(err.service(), Some("SVC"));
199 let err = err.with_tag("TAG");
200 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
201
202 let err2: Error<TestError> = Error::new(TestError::Service("409 Error"), "TEST");
203 assert_ne!(err, err2);
204 assert_eq!(err, TestError::Service("409 Error"));
205
206 let err2 = err2.with_tag("TAG");
207 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
208 let err2 = err2.with_service("SVC");
209 assert_eq!(err, err2);
210 let err2 = err2.map(|_| TestError::Disconnect);
211 assert_ne!(err, err2);
212 let err2 = err2.forward(|_| TestError::Disconnect);
213 assert_ne!(err, err2);
214
215 assert_eq!(TestError::Connect("").to_string(), "Connect err: ");
216 assert_eq!(TestError::Disconnect.to_string(), "Disconnect");
217 assert_eq!(TestError::Disconnect.service(), Some("test"));
218 assert!(TestError::Disconnect.backtrace().is_none());
219
220 assert_eq!(ResultType::ClientError.as_str(), "ClientError");
221 assert_eq!(ResultType::ServiceError.as_str(), "ServiceError");
222 assert_eq!(ResultType::ClientError.to_string(), "ClientError");
223 assert_eq!(ResultType::ServiceError.to_string(), "ServiceError");
224 assert_eq!(format!("{}", ResultType::ClientError), "ClientError");
225
226 assert_eq!(TestError::Connect("").signature(), "Client-Connect");
227 assert_eq!(TestError::Disconnect.signature(), "Client-Disconnect");
228 assert_eq!(TestError::Service("").signature(), "Service-Internal");
229
230 let err = err.into_error();
231 assert_eq!(err.to_string(), "InternalServiceError");
232 assert!(err.source().is_none());
233 assert!(format!("{err:?}").contains("Service(\"409 Error\")"));
234
235 #[cfg(unix)]
236 {
237 let err: Error<TestError> = TestError::Service("404 Error").into();
238 if let Some(bt) = err.backtrace() {
239 bt.resolver().resolve();
240 assert!(
241 format!("{bt}").contains("ntex_error::tests::test_error"),
242 "{bt}",
243 );
244 assert!(
245 bt.repr().unwrap().contains("ntex_error::tests::test_error"),
246 "{bt}"
247 );
248 }
249 }
250
251 assert_eq!(24, mem::size_of::<TestError>());
252 assert_eq!(8, mem::size_of::<Error<TestError>>());
253
254 assert_eq!(TestError2.service(), None);
255 assert_eq!(TestError2.signature(), "TestError2");
256
257 let err: Error<TestError> = TestError::Service("409 Error").into();
259 let msg = fmt_err_string(&err);
260 assert_eq!(msg, "InternalServiceError\n");
261 let msg = fmt_diag_string(&err);
262 assert!(msg.contains("err: InternalServiceError"));
263
264 let err: Failure = err.with_service("SVC").into();
265 assert_eq!(err.service(), Some("SVC"));
266 assert_eq!(err.signature(), "Service-Internal");
267 assert_eq!(err.as_diag().service(), Some("SVC"));
268 assert_eq!(err.as_diag().signature(), "Service-Internal");
269 assert!(err.backtrace().is_some());
270 assert!(err.as_diag().backtrace().is_some());
271
272 let res = Err(TestError::Service("409 Error"));
273 let res: Result<(), Error<TestError>> = res.into_error();
274 let _res: Result<(), Error<TestError2>> = res.into_error();
275
276 let msg = fmt_err_string(&err);
277 assert_eq!(msg, "InternalServiceError\n");
278
279 let err: Error<TestError> = TestError::Service("409 Error").into();
281 assert_eq!(err.get_item::<&str>(), None);
282 let err = err.with_item("Test");
283 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
284 let err2 = err.clone();
285 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
286 let err2 = err2.with_item("Test2");
287 assert_eq!(err2.get_item::<&str>(), Some(&"Test2"));
288 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
289 let err2 = err.clone().map(|_| TestError::Disconnect);
290 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
291
292 let info = Failure::from(&err2);
293 assert_eq!(info.get_item::<&str>(), Some(&"Test"));
294
295 let err3 = err
296 .clone()
297 .try_map(|_| Err::<(), _>(TestError2))
298 .err()
299 .unwrap();
300 assert_eq!(err3.signature(), "TestError2");
301 assert_eq!(err3.get_item::<&str>(), Some(&"Test"));
302
303 let res = err.clone().try_map(|_| Ok::<_, TestError2>(()));
304 assert_eq!(res, Ok(()));
305 assert_eq!(format!("{Success}"), "Success");
306
307 let res = Ok::<_, TestError>(());
308 let info = ResultSignature::from(&res);
309 assert_eq!(info.signature(), "Success");
310
311 let res = Err::<(), _>(TestError::Service("409 Error"));
312 let info = ResultSignature::from(&res);
313 assert_eq!(info.signature(), "Service-Internal");
314 }
315}