Skip to main content

ntex_error/
message.rs

1use std::{error::Error as StdError, fmt, rc::Rc};
2
3use ntex_bytes::ByteString;
4
5use crate::{ErrorDiagnostic, ResultKind, ResultType};
6
7pub fn fmt_err_string(e: &dyn StdError) -> String {
8    let mut buf = String::new();
9    _ = fmt_err(&mut buf, e);
10    buf
11}
12
13pub fn fmt_err(f: &mut dyn fmt::Write, e: &dyn StdError) -> fmt::Result {
14    let mut current = Some(e);
15    while let Some(std_err) = current {
16        writeln!(f, "{std_err}")?;
17        current = std_err.source();
18    }
19    Ok(())
20}
21
22pub fn fmt_diag_string<K: ResultKind>(e: &dyn ErrorDiagnostic<Kind = K>) -> String {
23    let mut buf = String::new();
24    _ = fmt_diag(&mut buf, e);
25    buf
26}
27
28pub fn fmt_diag<K>(f: &mut dyn fmt::Write, e: &dyn ErrorDiagnostic<Kind = K>) -> fmt::Result
29where
30    K: ResultKind,
31{
32    let k = e.kind();
33    let tp = k.tp();
34
35    writeln!(f, "err: {e}")?;
36    writeln!(f, "type: {}", tp.as_str())?;
37    writeln!(f, "signature: {}", k.signature())?;
38
39    if let Some(svc) = e.service() {
40        writeln!(f, "service: {svc}")?;
41    }
42
43    writeln!(f, "\n{e:?}")?;
44
45    let mut current = e.source();
46    while let Some(err) = current {
47        writeln!(f, "{err:?}")?;
48        current = err.source();
49    }
50
51    if tp == ResultType::ServiceError
52        && let Some(bt) = e.backtrace()
53    {
54        writeln!(f, "{bt}")?;
55    }
56
57    Ok(())
58}
59
60#[derive(Clone, PartialEq, Eq, thiserror::Error)]
61pub struct ErrorMessage(ByteString);
62
63#[derive(Clone)]
64pub struct ErrorMessageChained {
65    msg: ByteString,
66    source: Option<Rc<dyn StdError>>,
67}
68
69impl ErrorMessageChained {
70    pub fn new<M, E>(ctx: M, source: E) -> Self
71    where
72        M: Into<ErrorMessage>,
73        E: StdError + 'static,
74    {
75        ErrorMessageChained {
76            msg: ctx.into().into_string(),
77            source: Some(Rc::new(source)),
78        }
79    }
80
81    /// Construct `ErrorMessageChained` from `ByteString`
82    pub const fn from_bstr(msg: ByteString) -> Self {
83        Self { msg, source: None }
84    }
85
86    pub fn msg(&self) -> &ByteString {
87        &self.msg
88    }
89}
90
91impl ErrorMessage {
92    /// Construct new empty `ErrorMessage`
93    pub const fn empty() -> Self {
94        Self(ByteString::from_static(""))
95    }
96
97    /// Construct `ErrorMessage` from `ByteString`
98    pub const fn from_bstr(msg: ByteString) -> ErrorMessage {
99        ErrorMessage(msg)
100    }
101
102    /// Construct `ErrorMessage` from static string
103    pub const fn from_static(msg: &'static str) -> Self {
104        ErrorMessage(ByteString::from_static(msg))
105    }
106
107    pub fn is_empty(&self) -> bool {
108        self.0.is_empty()
109    }
110
111    pub fn as_str(&self) -> &str {
112        &self.0
113    }
114
115    pub fn as_bstr(&self) -> &ByteString {
116        &self.0
117    }
118
119    pub fn into_string(self) -> ByteString {
120        self.0
121    }
122
123    pub fn with_source<E: StdError + 'static>(self, source: E) -> ErrorMessageChained {
124        ErrorMessageChained::new(self, source)
125    }
126}
127
128impl From<String> for ErrorMessage {
129    fn from(value: String) -> Self {
130        Self(ByteString::from(value))
131    }
132}
133
134impl From<ByteString> for ErrorMessage {
135    fn from(value: ByteString) -> Self {
136        Self(value)
137    }
138}
139
140impl From<&'static str> for ErrorMessage {
141    fn from(value: &'static str) -> Self {
142        Self(ByteString::from_static(value))
143    }
144}
145
146impl fmt::Debug for ErrorMessage {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        fmt::Display::fmt(&self.0, f)
149    }
150}
151
152impl fmt::Display for ErrorMessage {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        fmt::Display::fmt(&self.0, f)
155    }
156}
157
158impl From<ErrorMessage> for ByteString {
159    fn from(msg: ErrorMessage) -> Self {
160        msg.0
161    }
162}
163
164impl<'a> From<&'a ErrorMessage> for ByteString {
165    fn from(msg: &'a ErrorMessage) -> Self {
166        msg.0.clone()
167    }
168}
169
170impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
171    fn from(value: M) -> Self {
172        ErrorMessageChained {
173            msg: value.into().0,
174            source: None,
175        }
176    }
177}
178
179impl StdError for ErrorMessageChained {
180    fn source(&self) -> Option<&(dyn StdError + 'static)> {
181        self.source.as_ref().map(AsRef::as_ref)
182    }
183}
184
185impl fmt::Debug for ErrorMessageChained {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        fmt::Display::fmt(&self, f)
188    }
189}
190
191impl fmt::Display for ErrorMessageChained {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        if self.msg.is_empty() {
194            Ok(())
195        } else {
196            fmt::Display::fmt(&self.msg, f)
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use std::io;
204
205    use super::*;
206
207    #[test]
208    fn error_message() {
209        let msg = ErrorMessage::empty();
210        assert!(msg.is_empty());
211        assert_eq!(msg.as_str(), "");
212        assert_eq!(msg.as_bstr(), ByteString::new());
213        assert_eq!(ByteString::new(), msg.as_bstr());
214        assert_eq!(ByteString::new(), msg.into_string());
215
216        let msg = ErrorMessage::from("test");
217        assert!(!msg.is_empty());
218        assert_eq!(format!("{msg}"), "test");
219        assert_eq!(msg.as_str(), "test");
220        assert_eq!(msg.as_bstr(), ByteString::from("test"));
221
222        let msg = ErrorMessage::from("test".to_string());
223        assert!(!msg.is_empty());
224        assert_eq!(msg.as_str(), "test");
225        assert_eq!(msg.as_bstr(), ByteString::from("test"));
226
227        let msg = ErrorMessage::from_bstr(ByteString::from("test"));
228        assert!(!msg.is_empty());
229        assert_eq!(msg.as_str(), "test");
230        assert_eq!(msg.as_bstr(), ByteString::from("test"));
231
232        let msg = ErrorMessage::from(ByteString::from("test"));
233        assert!(!msg.is_empty());
234        assert_eq!(msg.as_str(), "test");
235        assert_eq!(msg.as_bstr(), ByteString::from("test"));
236
237        let msg = ErrorMessage::from_static("test");
238        assert!(!msg.is_empty());
239        assert_eq!(msg.as_str(), "test");
240        assert_eq!(msg.as_bstr(), ByteString::from("test"));
241
242        assert_eq!(ByteString::from(&msg), "test");
243        assert_eq!(ByteString::from(msg), "test");
244    }
245
246    #[test]
247    fn error_message_chained() {
248        let chained = ErrorMessageChained::from(ByteString::from("test"));
249        assert_eq!(chained.msg(), "test");
250        assert!(chained.source().is_none());
251
252        let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
253        assert_eq!(chained.msg(), "test");
254        assert!(chained.source().is_none());
255        assert_eq!(format!("{chained}"), "test");
256        assert_eq!(format!("{chained:?}"), "test");
257
258        let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
259        let msg = fmt_err_string(&err);
260        assert_eq!(msg, "test\nio-test\n");
261    }
262}