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
75018
    pub(crate) fn capture() -> Captured {
21
75018
        Captured(Arc::new(Backtrace::force_capture()))
22
75018
    }
23

            
24
    impl Display for Captured {
25
2190
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26
2190
            Display::fmt(&self.0, f)
27
2190
        }
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
46895
    pub fn new<S: Into<String>>(kind: ErrorKind, message: S) -> Self {
94
46895
        Bug::new_inner(kind, message.into(), None)
95
46895
    }
96

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

            
124
    /// Adds context to an internal error or bug
125
    ///
126
    /// Prepends `prepend + ": "` to the message.
127
    //
128
    // The name is by analogy with `anyhow`, and matches the `BugContext` trait
129
    // (But distinguished to avoid mental and trait method clashes with `anyhow`
130
    // and make things clear at the call site.)
131
    pub fn bug_context(mut self, prepend: impl Display) -> Self {
132
        self.0.message = format!("{prepend}: {}", self.0.message);
133
        self
134
    }
135
}
136

            
137
impl std::error::Error for Bug {
138
594
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139
594
        self.0
140
594
            .source
141
594
            .as_deref()
142
597
            .map(|traitobj| traitobj as _ /* cast away Send and Sync */)
143
594
    }
144
}
145

            
146
impl Display for Bug {
147
2190
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148
2190
        writeln!(
149
2190
            f,
150
2190
            "{} at {}: {}",
151
2190
            self.0.kind, &self.0.location, &self.0.message
152
        )?;
153
2190
        Display::fmt(&self.0.backtrace, f)?;
154
2190
        Ok(())
155
2190
    }
156
}
157

            
158
/// Extension trait for `.bug_context()` on `Result`
159
pub trait BugContext: Sealed {
160
    /// Adds context to a internal error or bug
161
    ///
162
    /// Prepends `prepend + ": "` to the error message, if it's an error (`[Bug]`).
163
    fn bug_context<D: Display>(self, prefix: D) -> Self;
164
}
165
impl<T> BugContext for Result<T, Bug> {
166
    fn bug_context<D: Display>(self, prefix: D) -> Self {
167
        self.map_err(move |e| e.bug_context(prefix))
168
    }
169
}
170
impl<T> Sealed for Result<T, Bug> {}
171
/// Sealed (for `BugContext`)
172
// Separate from crate::sealed::Sealed because that has wide blanket impls which we don't want
173
mod sealed {
174
    /// Sealed
175
    pub trait Sealed {}
176
}
177
use sealed::Sealed;
178

            
179
/// Create an internal error, including a message like `format!`, and capturing this call site
180
///
181
/// The calling stack backtrace is also captured,
182
/// when the `backtrace` cargo feature this is enabled.
183
///
184
/// # Examples
185
///
186
/// ```
187
/// use tor_error::internal;
188
///
189
/// # fn main() -> Result<(), tor_error::Bug> {
190
/// # let mut cells = [()].iter();
191
/// let need_cell = cells.next().ok_or_else(|| internal!("no cells"))?;
192
/// # Ok(())
193
/// # }
194
/// ```
195
//
196
// In principle this macro could perhaps support internal!(from=source, "format", ...)
197
// but there are alternative ways of writing that:
198
//    Bug::new_from(source, format!(...)) or
199
//    into_internal!("format", ...)(source)
200
// Those are not so bad for what we think will be the rare cases not
201
// covered by internal!(...) or map_err(into_internal!(...))
202
#[macro_export]
203
macro_rules! internal {
204
    { $( $arg:tt )* } => {
205
        $crate::Bug::new($crate::ErrorKind::Internal, format!($($arg)*))
206
    }
207
}
208

            
209
/// Create a bad API usage error, including a message like `format!`, and capturing this call site
210
///
211
/// The calling stack backtrace is also captured,
212
/// when the `backtrace` cargo feature this is enabled.
213
///
214
/// # Examples
215
///
216
/// ```
217
/// use tor_error::bad_api_usage;
218
///
219
/// # fn main() -> Result<(), tor_error::Bug> {
220
/// # let mut targets = [()].iter();
221
/// let need_target = targets.next().ok_or_else(|| bad_api_usage!("no targets"))?;
222
/// # Ok(())
223
/// # }
224
#[macro_export]
225
macro_rules! bad_api_usage {
226
    { $( $arg:tt )* } => {
227
        $crate::Bug::new($crate::ErrorKind::BadApiUsage, format!($($arg)*))
228
    }
229
}
230

            
231
/// Helper for converting an error into an internal error
232
///
233
/// Returns a closure implementing `FnOnce(E) -> Bug`.
234
/// The source error `E` must be `std::error::Error + Send + Sync + 'static`.
235
///
236
/// # Examples
237
/// ```
238
/// use tor_error::into_internal;
239
///
240
/// # fn main() -> Result<(), tor_error::Bug> {
241
/// # let s = b"";
242
/// let s = std::str::from_utf8(s).map_err(into_internal!("bad bytes: {:?}", s))?;
243
/// # Ok(())
244
/// # }
245
/// ```
246
#[macro_export]
247
macro_rules! into_internal {
248
    { $( $arg:tt )* } => {
249
      std::convert::identity( // Hides the IEFI from clippy::redundant_closure_call
250
10
        |source| $crate::Bug::from_error($crate::ErrorKind::Internal, source, format!($($arg)*))
251
      )
252
    }
253
}
254

            
255
/// Helper for converting an error into an bad API usage error
256
///
257
/// Returns a closure implementing `FnOnce(E) -> InternalError`.
258
/// The source error `E` must be `std::error::Error + Send + Sync + 'static`.
259
///
260
/// # Examples
261
/// ```
262
/// use tor_error::into_bad_api_usage;
263
///
264
/// # fn main() -> Result<(), tor_error::Bug> {
265
/// # let host = b"";
266
/// let host = std::str::from_utf8(host).map_err(into_bad_api_usage!("hostname is bad UTF-8: {:?}", host))?;
267
/// # Ok(())
268
/// # }
269
/// ```
270
#[macro_export]
271
macro_rules! into_bad_api_usage {
272
    { $( $arg:tt )* } => {
273
      std::convert::identity( // Hides the IEFI from clippy::redundant_closure_call
274
2
        |source| $crate::Bug::from_error($crate::ErrorKind::BadApiUsage, source, format!($($arg)*))
275
      )
276
    }
277
}
278

            
279
impl HasKind for Bug {
280
672
    fn kind(&self) -> ErrorKind {
281
672
        self.0.kind
282
672
    }
283
}
284

            
285
#[cfg(test)]
286
mod test {
287
    // @@ begin test lint list maintained by maint/add_warning @@
288
    #![allow(clippy::bool_assert_comparison)]
289
    #![allow(clippy::clone_on_copy)]
290
    #![allow(clippy::dbg_macro)]
291
    #![allow(clippy::mixed_attributes_style)]
292
    #![allow(clippy::print_stderr)]
293
    #![allow(clippy::print_stdout)]
294
    #![allow(clippy::single_char_pattern)]
295
    #![allow(clippy::unwrap_used)]
296
    #![allow(clippy::unchecked_time_subtraction)]
297
    #![allow(clippy::useless_vec)]
298
    #![allow(clippy::needless_pass_by_value)]
299
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
300
    use super::*;
301

            
302
    // We test this on "important" and "reliable" platforms only.
303
    //
304
    // This test case mainly is to ensure that we are using the backtrace module correctly, etc.,
305
    // which can be checked by doing it on one platform.
306
    //
307
    // Doing the test on on *all* platforms would simply expose us to the vagaries of platform
308
    // backtrace support.  Arti ought not to fail its tests just because someone is using a
309
    // platform with poor backtrace support.
310
    //
311
    // On the other hand, we *do* want to know that things are correct on platforms where we think
312
    // Rust backtraces work properly.
313
    //
314
    // So this list is a compromise.  See
315
    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/509#note_2803085
316
    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
317
    #[test]
318
    #[inline(never)]
319
    fn internal_macro_test() {
320
        let start_of_func = line!();
321

            
322
        let e = internal!("Couldn't {} the {}.", "wobble", "wobbling device");
323
        assert_eq!(e.0.message, "Couldn't wobble the wobbling device.");
324
        assert!(e.0.location.file().ends_with("internal.rs"));
325
        assert!(e.0.location.line() > start_of_func);
326
        assert!(e.0.source.is_none());
327

            
328
        let s = e.to_string();
329
        dbg!(&s);
330

            
331
        assert!(s.starts_with("internal error (bug) at "));
332
        assert!(s.contains("Couldn't wobble the wobbling device."));
333
        #[cfg(feature = "backtrace")]
334
        assert!(s.contains("internal_macro_test"));
335

            
336
        #[derive(thiserror::Error, Debug)]
337
        enum Wrap {
338
            #[error("Internal error")]
339
            Internal(#[from] Bug),
340
        }
341

            
342
        let w: Wrap = e.into();
343
        let s = format!("Got: {}", w.report());
344
        dbg!(&s);
345
        assert!(s.contains("Couldn't wobble the wobbling device."));
346
    }
347

            
348
    #[test]
349
    fn source() {
350
        use std::error::Error;
351
        use std::str::FromStr;
352

            
353
        let start_of_func = line!();
354
        let s = "penguin";
355
        let inner = u32::from_str(s).unwrap_err();
356
        let outer = u32::from_str(s)
357
            .map_err(into_internal!("{} is not a number", s))
358
            .unwrap_err();
359

            
360
        let afterwards = line!();
361

            
362
        assert_eq!(outer.source().unwrap().to_string(), inner.to_string());
363
        assert!(outer.0.location.line() > start_of_func);
364
        assert!(outer.0.location.line() < afterwards);
365
    }
366
}