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