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