1
//! The InternalError type, macro for generating it, etc.
2

            
3
use std::fmt::{self, Debug, Display};
4
use std::panic;
5
use std::sync::Arc;
6

            
7
use super::*;
8

            
9
#[cfg(feature = "backtrace")]
10
/// Backtrace implementation for when the feature is enabled
11
mod ie_backtrace {
12
    use super::*;
13
    use backtrace::Backtrace;
14

            
15
249
    #[derive(Debug, Clone)]
16
    /// Captured backtrace, if turned on
17
    pub(crate) struct Captured(Backtrace);
18

            
19
    /// Capture a backtrace, if turned on
20
994
    pub(crate) fn capture() -> Captured {
21
994
        Captured(Backtrace::new())
22
994
    }
23

            
24
    impl Display for Captured {
25
249
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26
249
            Debug::fmt(self, f)
27
249
        }
28
    }
29
}
30

            
31
#[cfg(not(feature = "backtrace"))]
32
/// Backtrace implementation for when the feature is disabled
33
mod ie_backtrace {
34
    use super::*;
35

            
36
    #[derive(Debug, Clone)]
37
    /// "Captured backtrace", but actually nothing
38
    pub(crate) struct Captured;
39

            
40
    /// "Capture a backtrace", but actually return nothing
41
    pub(crate) fn capture() -> Captured {
42
        Captured
43
    }
44

            
45
    impl Display for Captured {
46
        fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
47
            Ok(())
48
        }
49
    }
50
}
51

            
52
4
#[derive(Debug, Clone)]
53
/// Programming error (a bug)
54
//
55
// Boxed because it is fairly large (>=12 words), and will be in a variant in many other errors.
56
//
57
// This is a single Bug type containing a kind in BugRepr, rather than separate InternalError and
58
// BadApiUsage types, primarily because that means that one Bug(#[from] tor_error::Bug) suffices in
59
// every crate's particular error type.
60
pub struct Bug(Box<BugRepr>);
61

            
62
/// The source of an Bug
63
type SourceError = Arc<dyn std::error::Error + Send + Sync + 'static>;
64

            
65
4
#[derive(Debug, Clone)]
66
/// Internal error (a bug)
67
struct BugRepr {
68
    /// Message, usually from internal!() like format!
69
    message: String,
70

            
71
    /// File and line number
72
    location: &'static panic::Location<'static>,
73

            
74
    /// Backtrace, perhaps
75
    backtrace: ie_backtrace::Captured,
76

            
77
    /// Source, perhaps
78
    source: Option<SourceError>,
79

            
80
    /// Kind
81
    ///
82
    /// `Internal` or `BadApiUsage`
83
    kind: ErrorKind,
84
}
85

            
86
impl Bug {
87
    /// Create a bug error report capturing this call site and backtrace
88
    ///
89
    /// Prefer to use [`internal!`],
90
    /// as that makes it easy to add additional information
91
    /// via format parameters.
92
    #[track_caller]
93
929
    pub fn new<S: Into<String>>(kind: ErrorKind, message: S) -> Self {
94
929
        Bug::new_inner(kind, message.into(), None)
95
929
    }
96

            
97
    /// Create an internal error
98
    #[track_caller]
99
994
    fn new_inner(kind: ErrorKind, message: String, source: Option<SourceError>) -> Self {
100
994
        Bug(BugRepr {
101
994
            kind,
102
994
            message,
103
994
            source,
104
994
            location: panic::Location::caller(),
105
994
            backtrace: ie_backtrace::capture(),
106
994
        }
107
994
        .into())
108
994
    }
109

            
110
    /// Create an bug error report from another error, capturing this call site and backtrace
111
    ///
112
    /// In `map_err`, and perhaps elsewhere, prefer to use [`into_internal!`],
113
    /// as that makes it easy to add additional information
114
    /// via format parameters.
115
    #[track_caller]
116
1
    pub fn from_error<E, S>(kind: ErrorKind, source: E, message: S) -> Self
117
1
    where
118
1
        S: Into<String>,
119
1
        E: std::error::Error + Send + Sync + 'static,
120
1
    {
121
1
        Bug::new_inner(kind, message.into(), Some(Arc::new(source)))
122
1
    }
123
}
124

            
125
impl std::error::Error for Bug {
126
1
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
127
1
        self.0
128
1
            .source
129
1
            .as_deref()
130
1
            .map(|traitobj| traitobj as _ /* cast away Send and Sync */)
131
1
    }
132
}
133

            
134
impl Display for Bug {
135
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136
249
        writeln!(
137
249
            f,
138
249
            "{} at {}: {}",
139
249
            self.0.kind, &self.0.location, &self.0.message
140
249
        )?;
141
249
        Display::fmt(&self.0.backtrace, f)?;
142
249
        Ok(())
143
249
    }
144
}
145

            
146
/// Create an internal error, including a message like `format!`, and capturing this call site
147
///
148
/// The calling stack backtrace is also captured,
149
/// when the `backtrace` cargo feature this is enabled.
150
///
151
/// # Examples
152
///
153
/// ```
154
/// use tor_error::internal;
155
///
156
/// # fn main() -> Result<(), tor_error::Bug> {
157
/// # let mut cells = [()].iter();
158
/// let need_cell = cells.next().ok_or_else(|| internal!("no cells"))?;
159
/// # Ok(())
160
/// # }
161
/// ```
162
//
163
// In principle this macro could perhaps support internal!(from=source, "format", ...)
164
// but there are alternative ways of writing that:
165
//    Bug::new_from(source, format!(...)) or
166
//    into_internal!("format", ...)(source)
167
// Those are not so bad for what we think will be the rare cases not
168
// covered by internal!(...) or map_err(into_internal!(...))
169
#[macro_export]
170
macro_rules! internal {
171
    { $( $arg:tt )* } => {
172
        $crate::Bug::new($crate::ErrorKind::Internal, format!($($arg)*))
173
    }
174
}
175

            
176
/// Create a bad API usage error, including a message like `format!`, and capturing this call site
177
///
178
/// The calling stack backtrace is also captured,
179
/// when the `backtrace` cargo feature this is enabled.
180
///
181
/// # Examples
182
///
183
/// ```
184
/// use tor_error::bad_api_usage;
185
///
186
/// # fn main() -> Result<(), tor_error::Bug> {
187
/// # let mut targets = [()].iter();
188
/// let need_target = targets.next().ok_or_else(|| bad_api_usage!("no targets"))?;
189
/// # Ok(())
190
/// # }
191
#[macro_export]
192
macro_rules! bad_api_usage {
193
    { $( $arg:tt )* } => {
194
        $crate::Bug::new($crate::ErrorKind::BadApiUsage, format!($($arg)*))
195
    }
196
}
197

            
198
/// Helper for converting an error into an internal error
199
///
200
/// Returns a closure implementing `FnOnce(E) -> Bug`.
201
/// The source error `E` must be `std::error::Error + Send + Sync + 'static`.
202
///
203
/// # Examples
204
/// ```
205
/// use tor_error::into_internal;
206
///
207
/// # fn main() -> Result<(), tor_error::Bug> {
208
/// # let s = b"";
209
/// let s = std::str::from_utf8(s).map_err(into_internal!("bad bytes: {:?}", s))?;
210
/// # Ok(())
211
/// # }
212
/// ```
213
#[macro_export]
214
macro_rules! into_internal {
215
    { $( $arg:tt )* } => {
216
        |source| $crate::Bug::from_error($crate::ErrorKind::Internal, source, format!($($arg)*))
217
    }
218
}
219

            
220
/// Helper for converting an error into an bad API usage error
221
///
222
/// Returns a closure implementing `FnOnce(E) -> InternalError`.
223
/// The source error `E` must be `std::error::Error + Send + Sync + 'static`.
224
///
225
/// # Examples
226
/// ```
227
/// use tor_error::into_bad_api_usage;
228
///
229
/// # fn main() -> Result<(), tor_error::Bug> {
230
/// # let host = b"";
231
/// let host = std::str::from_utf8(host).map_err(into_bad_api_usage!("hostname is bad UTF-8: {:?}", host))?;
232
/// # Ok(())
233
/// # }
234
/// ```
235
#[macro_export]
236
macro_rules! into_bad_api_usage {
237
    { $( $arg:tt )* } => {
238
        |source| $crate::Bug::from_error($crate::ErrorKind::BadApiUsage, source, format!($($arg)*))
239
    }
240
}
241

            
242
impl HasKind for Bug {
243
124
    fn kind(&self) -> ErrorKind {
244
124
        self.0.kind
245
124
    }
246
}
247

            
248
#[allow(clippy::unwrap_used)]
249
#[cfg(test)]
250
mod test {
251
    use super::*;
252

            
253
    #[test]
254
    fn internal_macro_test() {
255
        let start_of_func = line!();
256

            
257
        let e = internal!("Couldn't {} the {}.", "wobble", "wobbling device");
258
        assert_eq!(e.0.message, "Couldn't wobble the wobbling device.");
259
        assert!(e.0.location.file().ends_with("internal.rs"));
260
        assert!(e.0.location.line() > start_of_func);
261
        assert!(e.0.source.is_none());
262

            
263
        let s = e.to_string();
264

            
265
        assert!(s.starts_with("internal error (bug) at "));
266
        assert!(s.contains("Couldn't wobble the wobbling device."));
267
        #[cfg(feature = "backtrace")]
268
        assert!(s.contains("internal_macro_test"));
269
    }
270

            
271
    #[test]
272
    fn source() {
273
        use std::error::Error;
274
        use std::str::FromStr;
275

            
276
        let start_of_func = line!();
277
        let s = "penguin";
278
        let inner = u32::from_str(s).unwrap_err();
279
        let outer = u32::from_str(s)
280
            .map_err(into_internal!("{} is not a number", s))
281
            .unwrap_err();
282

            
283
        let afterwards = line!();
284

            
285
        assert_eq!(outer.source().unwrap().to_string(), inner.to_string());
286
        assert!(outer.0.location.line() > start_of_func);
287
        assert!(outer.0.location.line() < afterwards);
288
    }
289
}