1use std::{error::Error as StdError, fmt, fmt::Write, sync::Arc};
2
3use ntex_bytes::ByteString;
4
5use crate::{AsError, ErrorDiagnostic, ResultType};
6
7struct Wrt<'a> {
8 written: usize,
9 fmt: &'a mut dyn fmt::Write,
10}
11
12impl<'a> Wrt<'a> {
13 fn new(fmt: &'a mut dyn fmt::Write) -> Self {
14 Wrt { fmt, written: 0 }
15 }
16
17 fn wrote(&mut self) -> usize {
18 let res = self.written;
19 self.written = 0;
20 res
21 }
22}
23
24impl fmt::Write for Wrt<'_> {
25 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
26 self.written += s.len();
27 self.fmt.write_str(s)
28 }
29
30 fn write_char(&mut self, c: char) -> Result<(), fmt::Error> {
31 self.written += 1;
32 self.fmt.write_char(c)
33 }
34}
35
36pub fn fmt_err_string(e: &dyn StdError) -> String {
38 let mut buf = String::new();
39 _ = fmt_err(&mut buf, e);
40 buf
41}
42
43pub fn fmt_err(f: &mut dyn fmt::Write, e: &dyn StdError) -> fmt::Result {
45 let mut wrt = Wrt::new(f);
46 let mut current = Some(e);
47 while let Some(std_err) = current {
48 write!(&mut wrt, "{std_err}")?;
49 if wrt.wrote() > 0 {
50 writeln!(wrt.fmt)?;
51 }
52 current = std_err.source();
53 }
54 Ok(())
55}
56
57pub fn fmt_diag_string<'a, T>(e: &'a T) -> String
59where
60 T: ErrorDiagnostic + AsError,
61 ResultType: From<&'a T::Target>,
62{
63 let mut buf = String::new();
64 _ = fmt_diag(&mut buf, e);
65 buf
66}
67
68pub fn fmt_diag<'a, T>(f: &mut dyn fmt::Write, container: &'a T) -> fmt::Result
74where
75 T: ErrorDiagnostic + AsError,
76 ResultType: From<&'a T::Target>,
77{
78 fmt_diag_typ(f, Some(ResultType::from(container.as_diag())), container)
79}
80
81pub fn fmt_diag_typ<T>(f: &mut dyn fmt::Write, typ: Option<ResultType>, e: &T) -> fmt::Result
87where
88 T: ErrorDiagnostic,
89{
90 writeln!(f, "err: {e}")?;
91 if let Some(ref tp) = typ {
92 writeln!(f, "type: {}", tp.as_str())?;
93 }
94 writeln!(f, "signature: {}", e.signature())?;
95
96 if let Some(tag) = e.tag() {
97 if let Ok(s) = ByteString::try_from(tag) {
98 writeln!(f, "tag: {s}")?;
99 } else {
100 writeln!(f, "tag: {tag:?}")?;
101 }
102 }
103 if let Some(svc) = e.service() {
104 writeln!(f, "service: {svc}")?;
105 }
106 writeln!(f)?;
107
108 let mut wrt = Wrt::new(f);
109 write!(&mut wrt, "{e}")?;
110 if wrt.wrote() > 0 {
111 writeln!(wrt.fmt)?;
112 }
113
114 let mut nesting = 0;
115 let mut current = e.source();
116 while let Some(err) = current {
117 nesting += 2;
118 write!(&mut wrt, "{}", " ".repeat(nesting))?;
119 write!(&mut wrt, "{err}")?;
120 if wrt.wrote() > nesting {
121 writeln!(wrt.fmt)?;
122 }
123 current = err.source();
124 }
125
126 if typ == Some(ResultType::ServiceError)
127 && let Some(bt) = e.backtrace()
128 && let Some(repr) = bt.repr()
129 {
130 writeln!(wrt.fmt, "{repr}")?;
131 }
132
133 Ok(())
134}
135
136#[derive(Clone, PartialEq, Eq, thiserror::Error)]
138pub struct ErrorMessage(ByteString);
139
140#[derive(Clone)]
142pub struct ErrorMessageChained {
143 msg: ByteString,
144 source: Option<Arc<dyn StdError + Send + Sync>>,
145}
146
147impl ErrorMessageChained {
148 pub fn new<M, E>(ctx: M, source: E) -> Self
150 where
151 M: Into<ErrorMessage>,
152 E: StdError + Send + Sync + 'static,
153 {
154 ErrorMessageChained {
155 msg: ctx.into().into_string(),
156 source: Some(Arc::new(source)),
157 }
158 }
159
160 pub const fn empty() -> Self {
162 Self::from_static("")
163 }
164
165 pub const fn from_bstr(msg: ByteString) -> Self {
167 Self { msg, source: None }
168 }
169
170 pub const fn from_static(msg: &'static str) -> Self {
172 Self::from_bstr(ByteString::from_static(msg))
173 }
174
175 pub fn is_empty(&self) -> bool {
177 self.msg.is_empty()
178 }
179
180 pub fn as_str(&self) -> &str {
182 &self.msg
183 }
184
185 pub fn as_bstr(&self) -> &ByteString {
187 &self.msg
188 }
189
190 pub fn into_string(self) -> ByteString {
192 self.msg
193 }
194
195 #[must_use]
197 pub fn with_source<E: StdError + Send + Sync + 'static>(self, source: E) -> Self {
198 Self {
199 msg: self.msg,
200 source: Some(Arc::new(source)),
201 }
202 }
203}
204
205impl ErrorMessage {
206 pub const fn empty() -> Self {
208 Self(ByteString::from_static(""))
209 }
210
211 pub const fn from_bstr(msg: ByteString) -> Self {
213 ErrorMessage(msg)
214 }
215
216 pub const fn from_static(msg: &'static str) -> Self {
218 ErrorMessage(ByteString::from_static(msg))
219 }
220
221 pub fn is_empty(&self) -> bool {
223 self.0.is_empty()
224 }
225
226 pub fn as_str(&self) -> &str {
228 &self.0
229 }
230
231 pub fn as_bstr(&self) -> &ByteString {
233 &self.0
234 }
235
236 pub fn into_string(self) -> ByteString {
238 self.0
239 }
240
241 pub fn with_source<E: StdError + Send + Sync + 'static>(
243 self,
244 source: E,
245 ) -> ErrorMessageChained {
246 ErrorMessageChained::new(self, source)
247 }
248}
249
250impl From<String> for ErrorMessage {
251 fn from(value: String) -> Self {
252 Self(ByteString::from(value))
253 }
254}
255
256impl From<ByteString> for ErrorMessage {
257 fn from(value: ByteString) -> Self {
258 Self(value)
259 }
260}
261
262impl From<&'static str> for ErrorMessage {
263 fn from(value: &'static str) -> Self {
264 Self(ByteString::from_static(value))
265 }
266}
267
268impl fmt::Debug for ErrorMessage {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 fmt::Display::fmt(&self.0, f)
271 }
272}
273
274impl fmt::Display for ErrorMessage {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 fmt::Display::fmt(&self.0, f)
277 }
278}
279
280impl From<ErrorMessage> for ByteString {
281 fn from(msg: ErrorMessage) -> Self {
282 msg.0
283 }
284}
285
286impl<'a> From<&'a ErrorMessage> for ByteString {
287 fn from(msg: &'a ErrorMessage) -> Self {
288 msg.0.clone()
289 }
290}
291
292impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
293 fn from(value: M) -> Self {
294 ErrorMessageChained {
295 msg: value.into().0,
296 source: None,
297 }
298 }
299}
300
301impl StdError for ErrorMessageChained {
302 fn source(&self) -> Option<&(dyn StdError + 'static)> {
303 self.source
304 .as_ref()
305 .map(|e| e.as_ref() as &(dyn StdError + 'static))
306 }
307}
308
309impl fmt::Debug for ErrorMessageChained {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 fmt::Display::fmt(&self, f)
312 }
313}
314
315impl fmt::Display for ErrorMessageChained {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 if self.msg.is_empty() {
318 Ok(())
319 } else {
320 fmt::Display::fmt(&self.msg, f)
321 }
322 }
323}
324
325#[cfg(test)]
326#[allow(dead_code)]
327mod tests {
328 use ntex_bytes::Bytes;
329 use std::{error::Error, io};
330
331 use super::*;
332
333 #[test]
334 fn error_message() {
335 let msg = ErrorMessage::empty();
336 assert!(msg.is_empty());
337 assert_eq!(msg.as_str(), "");
338 assert_eq!(msg.as_bstr(), ByteString::new());
339 assert_eq!(ByteString::new(), msg.as_bstr());
340 assert_eq!(ByteString::new(), msg.into_string());
341
342 let msg = ErrorMessage::from("test");
343 assert!(!msg.is_empty());
344 assert_eq!(format!("{msg}"), "test");
345 assert_eq!(format!("{msg:?}"), "test");
346 assert_eq!(msg.as_str(), "test");
347 assert_eq!(msg.as_bstr(), ByteString::from("test"));
348
349 let msg = ErrorMessage::from("test".to_string());
350 assert!(!msg.is_empty());
351 assert_eq!(msg.as_str(), "test");
352 assert_eq!(msg.as_bstr(), ByteString::from("test"));
353
354 let msg = ErrorMessage::from_bstr(ByteString::from("test"));
355 assert!(!msg.is_empty());
356 assert_eq!(msg.as_str(), "test");
357 assert_eq!(msg.as_bstr(), ByteString::from("test"));
358
359 let msg = ErrorMessage::from(ByteString::from("test"));
360 assert!(!msg.is_empty());
361 assert_eq!(msg.as_str(), "test");
362 assert_eq!(msg.as_bstr(), ByteString::from("test"));
363
364 let msg = ErrorMessage::from_static("test");
365 assert!(!msg.is_empty());
366 assert_eq!(msg.as_str(), "test");
367 assert_eq!(msg.as_bstr(), ByteString::from("test"));
368
369 assert_eq!(ByteString::from(&msg), "test");
370 assert_eq!(ByteString::from(msg), "test");
371 }
372
373 #[test]
374 fn error_message_chained() {
375 let chained = ErrorMessageChained::from(ByteString::from("test"));
376 assert_eq!(chained.as_bstr(), "test");
377 assert!(chained.source().is_none());
378
379 let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
380 assert_eq!(chained.as_bstr(), "test");
381 assert!(chained.source().is_none());
382 assert_eq!(format!("{chained}"), "test");
383 assert_eq!(format!("{chained:?}"), "test");
384
385 let msg = ErrorMessage::from(ByteString::from("test"));
386 let chained = msg.with_source(io::Error::other("io-test"));
387 assert_eq!(chained.as_bstr(), "test");
388 assert!(chained.source().is_some());
389
390 let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
391 let msg = fmt_err_string(&err);
392 assert_eq!(msg, "test\nio-test\n");
393
394 let chained = ErrorMessageChained::from(ByteString::new());
395 assert_eq!(format!("{chained}"), "");
396 assert!(chained.is_empty());
397
398 let chained = ErrorMessageChained::empty();
399 assert!(chained.is_empty());
400 assert_eq!(chained.as_str(), "");
401 assert!(chained.source().is_none());
402
403 let chained = ErrorMessageChained::from_static("test");
404 assert!(!chained.is_empty());
405 assert_eq!(chained.as_str(), "test");
406 assert!(chained.source().is_none());
407
408 let chained = chained.with_source(io::Error::other("first"));
409 let chained = chained.with_source(io::Error::other("second"));
410 assert_eq!(chained.source().unwrap().to_string(), "second");
411 assert_eq!(chained.into_string(), "test");
412 }
413
414 #[derive(thiserror::Error, derive_more::Debug)]
415 enum TestError {
416 #[error("Disconnect")]
417 #[debug("")]
418 Disconnect(#[source] io::Error),
419 #[error("InternalServiceError")]
420 #[debug("InternalServiceError {_0}")]
421 Service(&'static str),
422 }
423
424 impl Clone for TestError {
425 fn clone(&self) -> Self {
426 panic!()
427 }
428 }
429
430 impl ErrorDiagnostic for TestError {
431 fn signature(&self) -> &'static str {
432 match self {
433 TestError::Service(_) => ResultType::ServiceError.as_str(),
434 TestError::Disconnect(_) => ResultType::ClientError.as_str(),
435 }
436 }
437 }
438
439 impl From<&TestError> for ResultType {
440 fn from(err: &TestError) -> ResultType {
441 match err {
442 TestError::Service(_) => ResultType::ServiceError,
443 TestError::Disconnect(_) => ResultType::ClientError,
444 }
445 }
446 }
447
448 #[test]
449 fn fmt_diag() {
450 let err = TestError::Service("409 Error");
451
452 let msg = fmt_err_string(&err);
453 assert_eq!(msg, "InternalServiceError\n");
454
455 let err = crate::Error::from(TestError::Disconnect(io::Error::other("Test io error")));
456 if let Some(bt) = err.backtrace() {
457 bt.resolver().resolve();
458 }
459 let msg = fmt_diag_string(&err);
460 assert!(msg.contains("Test io error"), "{msg}");
461
462 assert!(
463 format!("{:?}", err.source()).contains("Test io erro"),
464 "{:?}",
465 err.source().unwrap()
466 );
467
468 let err = err.with_tag(Bytes::from("test-tag"));
469 let msg = fmt_diag_string(&err);
470 assert!(msg.contains("test-tag"), "{msg}");
471 }
472}