1use std::{error, fmt, fmt::Write, rc::Rc};
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) -> 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<'a, T>(e: &'a T) -> String
57where
58 T: ErrorDiagnostic + AsError,
59 ResultType: From<&'a T::Target>,
60{
61 let mut buf = String::new();
62 _ = fmt_diag(&mut buf, e);
63 buf
64}
65
66pub fn fmt_diag<'a, T>(f: &mut dyn fmt::Write, container: &'a T) -> fmt::Result
71where
72 T: ErrorDiagnostic + AsError,
73 ResultType: From<&'a T::Target>,
74{
75 fmt_diag_typ(f, Some(ResultType::from(container.as_diag())), container)
76}
77
78pub fn fmt_diag_typ<T>(
83 f: &mut dyn fmt::Write,
84 typ: Option<ResultType>,
85 e: &T,
86) -> 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() {
111 writeln!(wrt.fmt)?;
112 }
113
114 let mut current = e.source();
115 while let Some(err) = current {
116 write!(&mut wrt, "{err:?}")?;
117 if wrt.wrote() {
118 writeln!(wrt.fmt)?;
119 }
120 current = err.source();
121 }
122
123 if typ == Some(ResultType::ServiceError)
124 && let Some(bt) = e.backtrace()
125 && let Some(repr) = bt.repr()
126 {
127 writeln!(wrt.fmt, "{repr}")?;
128 }
129
130 Ok(())
131}
132
133#[derive(Clone, PartialEq, Eq, thiserror::Error)]
134pub struct ErrorMessage(ByteString);
135
136#[derive(Clone)]
137pub struct ErrorMessageChained {
138 msg: ByteString,
139 source: Option<Rc<dyn error::Error>>,
140}
141
142impl ErrorMessageChained {
143 pub fn new<M, E>(ctx: M, source: E) -> Self
144 where
145 M: Into<ErrorMessage>,
146 E: error::Error + 'static,
147 {
148 ErrorMessageChained {
149 msg: ctx.into().into_string(),
150 source: Some(Rc::new(source)),
151 }
152 }
153
154 pub const fn from_bstr(msg: ByteString) -> Self {
156 Self { msg, source: None }
157 }
158
159 pub fn msg(&self) -> &ByteString {
160 &self.msg
161 }
162}
163
164impl ErrorMessage {
165 pub const fn empty() -> Self {
167 Self(ByteString::from_static(""))
168 }
169
170 pub const fn from_bstr(msg: ByteString) -> ErrorMessage {
172 ErrorMessage(msg)
173 }
174
175 pub const fn from_static(msg: &'static str) -> Self {
177 ErrorMessage(ByteString::from_static(msg))
178 }
179
180 pub fn is_empty(&self) -> bool {
181 self.0.is_empty()
182 }
183
184 pub fn as_str(&self) -> &str {
185 &self.0
186 }
187
188 pub fn as_bstr(&self) -> &ByteString {
189 &self.0
190 }
191
192 pub fn into_string(self) -> ByteString {
193 self.0
194 }
195
196 pub fn with_source<E: error::Error + 'static>(self, source: E) -> ErrorMessageChained {
197 ErrorMessageChained::new(self, source)
198 }
199}
200
201impl From<String> for ErrorMessage {
202 fn from(value: String) -> Self {
203 Self(ByteString::from(value))
204 }
205}
206
207impl From<ByteString> for ErrorMessage {
208 fn from(value: ByteString) -> Self {
209 Self(value)
210 }
211}
212
213impl From<&'static str> for ErrorMessage {
214 fn from(value: &'static str) -> Self {
215 Self(ByteString::from_static(value))
216 }
217}
218
219impl fmt::Debug for ErrorMessage {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 fmt::Display::fmt(&self.0, f)
222 }
223}
224
225impl fmt::Display for ErrorMessage {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 fmt::Display::fmt(&self.0, f)
228 }
229}
230
231impl From<ErrorMessage> for ByteString {
232 fn from(msg: ErrorMessage) -> Self {
233 msg.0
234 }
235}
236
237impl<'a> From<&'a ErrorMessage> for ByteString {
238 fn from(msg: &'a ErrorMessage) -> Self {
239 msg.0.clone()
240 }
241}
242
243impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
244 fn from(value: M) -> Self {
245 ErrorMessageChained {
246 msg: value.into().0,
247 source: None,
248 }
249 }
250}
251
252impl error::Error for ErrorMessageChained {
253 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
254 self.source.as_ref().map(AsRef::as_ref)
255 }
256}
257
258impl fmt::Debug for ErrorMessageChained {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt::Display::fmt(&self, f)
261 }
262}
263
264impl fmt::Display for ErrorMessageChained {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 if self.msg.is_empty() {
267 Ok(())
268 } else {
269 fmt::Display::fmt(&self.msg, f)
270 }
271 }
272}
273
274#[cfg(test)]
275#[allow(dead_code)]
276mod tests {
277 use ntex_bytes::Bytes;
278 use std::{error::Error, io};
279
280 use super::*;
281
282 #[test]
283 fn error_message() {
284 let msg = ErrorMessage::empty();
285 assert!(msg.is_empty());
286 assert_eq!(msg.as_str(), "");
287 assert_eq!(msg.as_bstr(), ByteString::new());
288 assert_eq!(ByteString::new(), msg.as_bstr());
289 assert_eq!(ByteString::new(), msg.into_string());
290
291 let msg = ErrorMessage::from("test");
292 assert!(!msg.is_empty());
293 assert_eq!(format!("{msg}"), "test");
294 assert_eq!(format!("{msg:?}"), "test");
295 assert_eq!(msg.as_str(), "test");
296 assert_eq!(msg.as_bstr(), ByteString::from("test"));
297
298 let msg = ErrorMessage::from("test".to_string());
299 assert!(!msg.is_empty());
300 assert_eq!(msg.as_str(), "test");
301 assert_eq!(msg.as_bstr(), ByteString::from("test"));
302
303 let msg = ErrorMessage::from_bstr(ByteString::from("test"));
304 assert!(!msg.is_empty());
305 assert_eq!(msg.as_str(), "test");
306 assert_eq!(msg.as_bstr(), ByteString::from("test"));
307
308 let msg = ErrorMessage::from(ByteString::from("test"));
309 assert!(!msg.is_empty());
310 assert_eq!(msg.as_str(), "test");
311 assert_eq!(msg.as_bstr(), ByteString::from("test"));
312
313 let msg = ErrorMessage::from_static("test");
314 assert!(!msg.is_empty());
315 assert_eq!(msg.as_str(), "test");
316 assert_eq!(msg.as_bstr(), ByteString::from("test"));
317
318 assert_eq!(ByteString::from(&msg), "test");
319 assert_eq!(ByteString::from(msg), "test");
320 }
321
322 #[test]
323 fn error_message_chained() {
324 let chained = ErrorMessageChained::from(ByteString::from("test"));
325 assert_eq!(chained.msg(), "test");
326 assert!(chained.source().is_none());
327
328 let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
329 assert_eq!(chained.msg(), "test");
330 assert!(chained.source().is_none());
331 assert_eq!(format!("{chained}"), "test");
332 assert_eq!(format!("{chained:?}"), "test");
333
334 let msg = ErrorMessage::from(ByteString::from("test"));
335 let chained = msg.with_source(io::Error::other("io-test"));
336 assert_eq!(chained.msg(), "test");
337 assert!(chained.source().is_some());
338
339 let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
340 let msg = fmt_err_string(&err);
341 assert_eq!(msg, "test\nio-test\n");
342
343 let chained = ErrorMessageChained::from(ByteString::new());
344 assert_eq!(format!("{chained}"), "");
345 }
346
347 #[derive(thiserror::Error, derive_more::Debug)]
348 enum TestError {
349 #[error("Disconnect")]
350 #[debug("")]
351 Disconnect(#[source] io::Error),
352 #[error("InternalServiceError")]
353 #[debug("InternalServiceError {_0}")]
354 Service(&'static str),
355 }
356
357 impl Clone for TestError {
358 fn clone(&self) -> Self {
359 panic!()
360 }
361 }
362
363 impl ErrorDiagnostic for TestError {
364 fn signature(&self) -> &'static str {
365 match self {
366 TestError::Service(_) => ResultType::ServiceError.as_str(),
367 TestError::Disconnect(_) => ResultType::ClientError.as_str(),
368 }
369 }
370 }
371
372 impl From<&TestError> for ResultType {
373 fn from(err: &TestError) -> ResultType {
374 match err {
375 TestError::Service(_) => ResultType::ServiceError,
376 TestError::Disconnect(_) => ResultType::ClientError,
377 }
378 }
379 }
380
381 #[test]
382 fn fmt_diag() {
383 let err = TestError::Service("409 Error");
384
385 let msg = fmt_err_string(&err);
386 assert_eq!(msg, "InternalServiceError\n");
387
388 let err =
389 crate::Error::from(TestError::Disconnect(io::Error::other("Test io error")));
390 if let Some(bt) = err.backtrace() {
391 bt.resolver().resolve();
392 }
393 let msg = fmt_diag_string(&err);
394 assert!(msg.contains("Test io error"), "{msg}");
395
396 assert!(
397 format!("{:?}", err.source()).contains("Test io erro"),
398 "{:?}",
399 err.source().unwrap()
400 );
401
402 let err = err.set_tag(Bytes::from("test-tag"));
403 let msg = fmt_diag_string(&err);
404 assert!(msg.contains("test-tag"), "{msg}");
405 }
406}