Skip to main content

ntex_error/
message.rs

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