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(all(feature = "backtrace", not(miri)))]
10
/// Backtrace implementation for when the feature is enabled
11
mod ie_backtrace {
12
    use super::*;
13
    use std::backtrace::Backtrace;
14

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

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

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

            
31
#[cfg(any(not(feature = "backtrace"), miri))]
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
#[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
#[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
107248
    pub fn new<S: Into<String>>(kind: ErrorKind, message: S) -> Self {
94
107248
        Bug::new_inner(kind, message.into(), None)
95
107248
    }
96

            
97
    /// Create an internal error
98
    #[track_caller]
99
114124
    fn new_inner(kind: ErrorKind, message: String, source: Option<SourceError>) -> Self {
100
114124
        Bug(BugRepr {
101
114124
            kind,
102
114124
            message,
103
114124
            source,
104
114124
            location: panic::Location::caller(),
105
114124
            backtrace: ie_backtrace::capture(),
106
114124
        }
107
114124
        .into())
108
114124
    }
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
8
    pub fn from_error<E, S>(kind: ErrorKind, source: E, message: S) -> Self
117
8
    where
118
8
        S: Into<String>,
119
8
        E: std::error::Error + Send + Sync + 'static,
120
8
    {
121
8
        Bug::new_inner(kind, message.into(), Some(Arc::new(source)))
122
8
    }
123
}
124

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

            
134
impl Display for Bug {
135
724
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136
724
        writeln!(
137
724
            f,
138
724
            "{} at {}: {}",
139
724
            self.0.kind, &self.0.location, &self.0.message
140
724
        )?;
141
724
        Display::fmt(&self.0.backtrace, f)?;
142
724
        Ok(())
143
724
    }
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
      std::convert::identity( // Hides the IEFI from clippy::redundant_closure_call
217
6
        |source| $crate::Bug::from_error($crate::ErrorKind::Internal, source, format!($($arg)*))
218
      )
219
    }
220
}
221

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

            
246
impl HasKind for Bug {
247
576
    fn kind(&self) -> ErrorKind {
248
576
        self.0.kind
249
576
    }
250
}
251

            
252
#[cfg(test)]
253
mod test {
254
    // @@ begin test lint list maintained by maint/add_warning @@
255
    #![allow(clippy::bool_assert_comparison)]
256
    #![allow(clippy::clone_on_copy)]
257
    #![allow(clippy::dbg_macro)]
258
    #![allow(clippy::mixed_attributes_style)]
259
    #![allow(clippy::print_stderr)]
260
    #![allow(clippy::print_stdout)]
261
    #![allow(clippy::single_char_pattern)]
262
    #![allow(clippy::unwrap_used)]
263
    #![allow(clippy::unchecked_duration_subtraction)]
264
    #![allow(clippy::useless_vec)]
265
    #![allow(clippy::needless_pass_by_value)]
266
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
267
    use super::*;
268

            
269
    // We test this on "important" and "reliable" platforms only.
270
    //
271
    // This test case mainly is to ensure that we are using the backtrace module correctly, etc.,
272
    // which can be checked by doing it on one platform.
273
    //
274
    // Doing the test on on *all* platforms would simply expose us to the vagaries of platform
275
    // backtrace support.  Arti ought not to fail its tests just because someone is using a
276
    // platform with poor backtrace support.
277
    //
278
    // On the other hand, we *do* want to know that things are correct on platforms where we think
279
    // Rust backtraces work properly.
280
    //
281
    // So this list is a compromise.  See
282
    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/509#note_2803085
283
    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
284
    #[test]
285
    #[inline(never)]
286
    fn internal_macro_test() {
287
        let start_of_func = line!();
288

            
289
        let e = internal!("Couldn't {} the {}.", "wobble", "wobbling device");
290
        assert_eq!(e.0.message, "Couldn't wobble the wobbling device.");
291
        assert!(e.0.location.file().ends_with("internal.rs"));
292
        assert!(e.0.location.line() > start_of_func);
293
        assert!(e.0.source.is_none());
294

            
295
        let s = e.to_string();
296
        dbg!(&s);
297

            
298
        assert!(s.starts_with("internal error (bug) at "));
299
        assert!(s.contains("Couldn't wobble the wobbling device."));
300
        #[cfg(feature = "backtrace")]
301
        assert!(s.contains("internal_macro_test"));
302

            
303
        #[derive(thiserror::Error, Debug)]
304
        enum Wrap {
305
            #[error("Internal error")]
306
            Internal(#[from] Bug),
307
        }
308

            
309
        let w: Wrap = e.into();
310
        let s = format!("Got: {}", w.report());
311
        dbg!(&s);
312
        assert!(s.contains("Couldn't wobble the wobbling device."));
313
    }
314

            
315
    #[test]
316
    fn source() {
317
        use std::error::Error;
318
        use std::str::FromStr;
319

            
320
        let start_of_func = line!();
321
        let s = "penguin";
322
        let inner = u32::from_str(s).unwrap_err();
323
        let outer = u32::from_str(s)
324
            .map_err(into_internal!("{} is not a number", s))
325
            .unwrap_err();
326

            
327
        let afterwards = line!();
328

            
329
        assert_eq!(outer.source().unwrap().to_string(), inner.to_string());
330
        assert!(outer.0.location.line() > start_of_func);
331
        assert!(outer.0.location.line() < afterwards);
332
    }
333
}