1use std::{error, fmt, fmt::Write, rc::Rc};
2
3use ntex_bytes::ByteString;
4
5use crate::{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) -> bool {
18 let res = self.written != 0;
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 error::Error) -> String {
37 let mut buf = String::new();
38 _ = fmt_err(&mut buf, e);
39 buf
40}
41
42pub fn fmt_err(f: &mut dyn fmt::Write, e: &dyn error::Error) -> fmt::Result {
43 let mut wrt = Wrt::new(f);
44 let mut current = Some(e);
45 while let Some(std_err) = current {
46 write!(&mut wrt, "{std_err}")?;
47 if wrt.wrote() {
48 writeln!(wrt.fmt)?;
49 }
50 current = std_err.source();
51 }
52 Ok(())
53}
54
55pub fn fmt_diag_string(e: &dyn ErrorDiagnostic) -> String {
57 let mut buf = String::new();
58 _ = fmt_diag(&mut buf, e);
59 buf
60}
61
62pub fn fmt_diag(f: &mut dyn fmt::Write, e: &dyn ErrorDiagnostic) -> fmt::Result {
67 writeln!(f, "err: {e}")?;
68 writeln!(f, "type: {}", e.typ())?;
69 writeln!(f, "signature: {}", e.signature())?;
70
71 if let Some(tag) = e.tag() {
72 if let Ok(s) = ByteString::try_from(tag) {
73 writeln!(f, "tag: {s}")?;
74 } else {
75 writeln!(f, "tag: {tag:?}")?;
76 }
77 }
78 if let Some(svc) = e.service() {
79 writeln!(f, "service: {svc}")?;
80 }
81 writeln!(f)?;
82
83 let mut wrt = Wrt::new(f);
84 write!(&mut wrt, "{e:?}")?;
85 if wrt.wrote() {
86 writeln!(wrt.fmt)?;
87 }
88
89 let mut current = e.source();
90 while let Some(err) = current {
91 write!(&mut wrt, "{err:?}")?;
92 if wrt.wrote() {
93 writeln!(wrt.fmt)?;
94 }
95 current = err.source();
96 }
97
98 if e.typ() == ResultType::ServiceError
99 && let Some(bt) = e.backtrace()
100 && let Some(repr) = bt.repr()
101 {
102 writeln!(wrt.fmt, "{repr}")?;
103 }
104
105 Ok(())
106}
107
108#[derive(Clone, PartialEq, Eq, thiserror::Error)]
109pub struct ErrorMessage(ByteString);
110
111#[derive(Clone)]
112pub struct ErrorMessageChained {
113 msg: ByteString,
114 source: Option<Rc<dyn error::Error>>,
115}
116
117impl ErrorMessageChained {
118 pub fn new<M, E>(ctx: M, source: E) -> Self
119 where
120 M: Into<ErrorMessage>,
121 E: error::Error + 'static,
122 {
123 ErrorMessageChained {
124 msg: ctx.into().into_string(),
125 source: Some(Rc::new(source)),
126 }
127 }
128
129 pub const fn from_bstr(msg: ByteString) -> Self {
131 Self { msg, source: None }
132 }
133
134 pub fn msg(&self) -> &ByteString {
135 &self.msg
136 }
137}
138
139impl ErrorMessage {
140 pub const fn empty() -> Self {
142 Self(ByteString::from_static(""))
143 }
144
145 pub const fn from_bstr(msg: ByteString) -> ErrorMessage {
147 ErrorMessage(msg)
148 }
149
150 pub const fn from_static(msg: &'static str) -> Self {
152 ErrorMessage(ByteString::from_static(msg))
153 }
154
155 pub fn is_empty(&self) -> bool {
156 self.0.is_empty()
157 }
158
159 pub fn as_str(&self) -> &str {
160 &self.0
161 }
162
163 pub fn as_bstr(&self) -> &ByteString {
164 &self.0
165 }
166
167 pub fn into_string(self) -> ByteString {
168 self.0
169 }
170
171 pub fn with_source<E: error::Error + 'static>(self, source: E) -> ErrorMessageChained {
172 ErrorMessageChained::new(self, source)
173 }
174}
175
176impl From<String> for ErrorMessage {
177 fn from(value: String) -> Self {
178 Self(ByteString::from(value))
179 }
180}
181
182impl From<ByteString> for ErrorMessage {
183 fn from(value: ByteString) -> Self {
184 Self(value)
185 }
186}
187
188impl From<&'static str> for ErrorMessage {
189 fn from(value: &'static str) -> Self {
190 Self(ByteString::from_static(value))
191 }
192}
193
194impl fmt::Debug for ErrorMessage {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 fmt::Display::fmt(&self.0, f)
197 }
198}
199
200impl fmt::Display for ErrorMessage {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt::Display::fmt(&self.0, f)
203 }
204}
205
206impl From<ErrorMessage> for ByteString {
207 fn from(msg: ErrorMessage) -> Self {
208 msg.0
209 }
210}
211
212impl<'a> From<&'a ErrorMessage> for ByteString {
213 fn from(msg: &'a ErrorMessage) -> Self {
214 msg.0.clone()
215 }
216}
217
218impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
219 fn from(value: M) -> Self {
220 ErrorMessageChained {
221 msg: value.into().0,
222 source: None,
223 }
224 }
225}
226
227impl error::Error for ErrorMessageChained {
228 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
229 self.source.as_ref().map(AsRef::as_ref)
230 }
231}
232
233impl fmt::Debug for ErrorMessageChained {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 fmt::Display::fmt(&self, f)
236 }
237}
238
239impl fmt::Display for ErrorMessageChained {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 if self.msg.is_empty() {
242 Ok(())
243 } else {
244 fmt::Display::fmt(&self.msg, f)
245 }
246 }
247}
248
249#[cfg(test)]
250#[allow(dead_code)]
251mod tests {
252 use ntex_bytes::Bytes;
253 use std::{error::Error, io};
254
255 use super::*;
256
257 #[test]
258 fn error_message() {
259 let msg = ErrorMessage::empty();
260 assert!(msg.is_empty());
261 assert_eq!(msg.as_str(), "");
262 assert_eq!(msg.as_bstr(), ByteString::new());
263 assert_eq!(ByteString::new(), msg.as_bstr());
264 assert_eq!(ByteString::new(), msg.into_string());
265
266 let msg = ErrorMessage::from("test");
267 assert!(!msg.is_empty());
268 assert_eq!(format!("{msg}"), "test");
269 assert_eq!(format!("{msg:?}"), "test");
270 assert_eq!(msg.as_str(), "test");
271 assert_eq!(msg.as_bstr(), ByteString::from("test"));
272
273 let msg = ErrorMessage::from("test".to_string());
274 assert!(!msg.is_empty());
275 assert_eq!(msg.as_str(), "test");
276 assert_eq!(msg.as_bstr(), ByteString::from("test"));
277
278 let msg = ErrorMessage::from_bstr(ByteString::from("test"));
279 assert!(!msg.is_empty());
280 assert_eq!(msg.as_str(), "test");
281 assert_eq!(msg.as_bstr(), ByteString::from("test"));
282
283 let msg = ErrorMessage::from(ByteString::from("test"));
284 assert!(!msg.is_empty());
285 assert_eq!(msg.as_str(), "test");
286 assert_eq!(msg.as_bstr(), ByteString::from("test"));
287
288 let msg = ErrorMessage::from_static("test");
289 assert!(!msg.is_empty());
290 assert_eq!(msg.as_str(), "test");
291 assert_eq!(msg.as_bstr(), ByteString::from("test"));
292
293 assert_eq!(ByteString::from(&msg), "test");
294 assert_eq!(ByteString::from(msg), "test");
295 }
296
297 #[test]
298 fn error_message_chained() {
299 let chained = ErrorMessageChained::from(ByteString::from("test"));
300 assert_eq!(chained.msg(), "test");
301 assert!(chained.source().is_none());
302
303 let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
304 assert_eq!(chained.msg(), "test");
305 assert!(chained.source().is_none());
306 assert_eq!(format!("{chained}"), "test");
307 assert_eq!(format!("{chained:?}"), "test");
308
309 let msg = ErrorMessage::from(ByteString::from("test"));
310 let chained = msg.with_source(io::Error::other("io-test"));
311 assert_eq!(chained.msg(), "test");
312 assert!(chained.source().is_some());
313
314 let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
315 let msg = fmt_err_string(&err);
316 assert_eq!(msg, "test\nio-test\n");
317
318 let chained = ErrorMessageChained::from(ByteString::new());
319 assert_eq!(format!("{chained}"), "");
320 }
321
322 #[derive(thiserror::Error, derive_more::Debug)]
323 enum TestError {
324 #[error("Disconnect")]
325 #[debug("")]
326 Disconnect(#[source] io::Error),
327 #[error("InternalServiceError")]
328 #[debug("InternalServiceError {_0}")]
329 Service(&'static str),
330 }
331
332 impl Clone for TestError {
333 fn clone(&self) -> Self {
334 panic!()
335 }
336 }
337
338 impl ErrorDiagnostic for TestError {
339 fn typ(&self) -> ResultType {
340 match self {
341 TestError::Service(_) => ResultType::ServiceError,
342 TestError::Disconnect(_) => ResultType::ClientError,
343 }
344 }
345
346 fn signature(&self) -> &'static str {
347 self.typ().as_str()
348 }
349 }
350
351 #[test]
352 fn fmt_diag() {
353 let err = TestError::Service("409 Error");
354
355 let msg = fmt_err_string(&err);
356 assert_eq!(msg, "InternalServiceError\n");
357
358 let err =
359 crate::Error::from(TestError::Disconnect(io::Error::other("Test io error")));
360 if let Some(bt) = err.backtrace() {
361 bt.resolver().resolve();
362 }
363 let msg = fmt_diag_string(&err);
364 assert!(msg.contains("Test io error"), "{msg}");
365
366 assert!(
367 format!("{:?}", err.source()).contains("Test io erro"),
368 "{:?}",
369 err.source().unwrap()
370 );
371
372 let err = err.set_tag(Bytes::from("test-tag"));
373 let msg = fmt_diag_string(&err);
374 assert!(msg.contains("test-tag"), "{msg}");
375 }
376}