2023-06-29 00:00:42 -04:00
|
|
|
use core::panic::Location;
|
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
use std::backtrace::BacktraceStatus;
|
|
|
|
|
2022-11-09 19:48:43 -05:00
|
|
|
use crate::*;
|
|
|
|
|
|
|
|
use crate::report::report_write;
|
|
|
|
|
2023-06-29 01:51:08 -04:00
|
|
|
/// The error type.
|
2023-06-29 00:00:42 -04:00
|
|
|
///
|
|
|
|
/// By default, does not implement [`Clone`] because [`std::backtrace::Backtrace`] does not
|
|
|
|
/// implement [`Clone`]. However, the `clone-with-caveats` feature may be used to enable a
|
|
|
|
/// [`Clone`] impl that sets the cloned `backtrace` to [`std::backtrace::Backtrace::disabled`].
|
2022-11-09 19:48:43 -05:00
|
|
|
pub struct How(Box<HowInner>);
|
|
|
|
|
|
|
|
struct HowInner {
|
2023-06-29 00:00:42 -04:00
|
|
|
location: &'static Location<'static>,
|
2022-11-09 19:48:43 -05:00
|
|
|
#[cfg(feature = "backtrace")]
|
2023-06-29 00:00:42 -04:00
|
|
|
backtrace: Backtrace,
|
2022-11-09 19:48:43 -05:00
|
|
|
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
|
|
|
|
// Probably move after `backtrace`)
|
|
|
|
context: Vec<Context>,
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:00:42 -04:00
|
|
|
// will be replaced with std::backtrace::Backtrace if and when it is Clone
|
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Backtrace {
|
|
|
|
Disabled,
|
|
|
|
Unsupported,
|
|
|
|
Other(BacktraceStatus, String),
|
|
|
|
}
|
|
|
|
|
2022-11-09 19:48:43 -05:00
|
|
|
impl How {
|
|
|
|
#[must_use]
|
|
|
|
#[inline(never)]
|
2023-01-23 19:06:23 -05:00
|
|
|
#[track_caller]
|
2022-11-09 19:48:43 -05:00
|
|
|
pub fn new(context: impl IntoContext) -> Self {
|
2023-06-29 00:00:42 -04:00
|
|
|
let location = Location::caller();
|
2022-11-09 19:48:43 -05:00
|
|
|
Self(Box::new(HowInner {
|
2023-06-29 00:00:42 -04:00
|
|
|
location,
|
|
|
|
context: Vec::with_capacity(4),
|
2022-11-09 19:48:43 -05:00
|
|
|
#[cfg(feature = "backtrace")]
|
2023-06-29 00:00:42 -04:00
|
|
|
backtrace: {
|
|
|
|
let bt = std::backtrace::Backtrace::capture();
|
|
|
|
match bt.status() {
|
|
|
|
BacktraceStatus::Disabled => Backtrace::Disabled,
|
|
|
|
BacktraceStatus::Unsupported => Backtrace::Unsupported,
|
|
|
|
status => Backtrace::Other(status, bt.to_string()),
|
|
|
|
}
|
|
|
|
},
|
2022-11-09 19:48:43 -05:00
|
|
|
}))
|
2023-06-29 01:51:08 -04:00
|
|
|
.context(context)
|
2022-11-09 19:48:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn clone_without_backtrace(&self) -> Self {
|
|
|
|
Self(Box::new(HowInner {
|
2023-06-29 00:00:42 -04:00
|
|
|
location: self.0.location,
|
2022-11-09 19:48:43 -05:00
|
|
|
context: self.0.context.clone(),
|
|
|
|
#[cfg(feature = "backtrace")]
|
2023-06-29 00:00:42 -04:00
|
|
|
backtrace: Backtrace::Disabled,
|
2022-11-09 19:48:43 -05:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:00:42 -04:00
|
|
|
pub fn location(&self) -> &'static Location {
|
|
|
|
self.0.location
|
2023-02-06 09:09:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn top(&self) -> &Context {
|
|
|
|
// SAFETY: we only ever push values into context, and the constructor ensures that there
|
2023-06-29 00:00:42 -04:00
|
|
|
// is at least 1 value in context.
|
|
|
|
let o = self.0.context.iter().next();
|
2023-02-06 09:09:01 -05:00
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
o.unwrap()
|
|
|
|
} else {
|
|
|
|
#[allow(unsafe_code)]
|
2023-06-29 00:00:42 -04:00
|
|
|
unsafe {
|
|
|
|
o.unwrap_unchecked()
|
|
|
|
}
|
2023-02-06 09:09:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn bottom(&self) -> &Context {
|
|
|
|
// SAFETY: we only ever push values into context, and the constructor ensures that there
|
2023-06-29 00:00:42 -04:00
|
|
|
// is at least 1 value in context.
|
2023-02-06 09:09:01 -05:00
|
|
|
let o = self.0.context.iter().next_back();
|
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
o.unwrap()
|
|
|
|
} else {
|
|
|
|
#[allow(unsafe_code)]
|
2023-06-29 00:00:42 -04:00
|
|
|
unsafe {
|
|
|
|
o.unwrap_unchecked()
|
|
|
|
}
|
2023-02-06 09:09:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn into_context(self) -> impl Iterator<Item = Context> {
|
|
|
|
self.0.context.into_iter()
|
|
|
|
}
|
|
|
|
|
2023-01-17 07:29:58 -05:00
|
|
|
fn fmt_debug_alternate(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
let mut b = f.debug_struct(std::any::type_name::<Self>());
|
2023-06-29 00:00:42 -04:00
|
|
|
b.field("location", &(&self.0.location));
|
|
|
|
b.field("context", &(&self.0.context));
|
2023-01-17 07:29:58 -05:00
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
let b = b.field("backtrace", &self.0.backtrace);
|
|
|
|
b.finish()
|
|
|
|
}
|
2023-06-29 01:51:08 -04:00
|
|
|
|
|
|
|
pub(crate) fn push_context(&mut self, context: Context) {
|
|
|
|
self.0.context.push(context);
|
|
|
|
}
|
2022-11-09 19:48:43 -05:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:00:42 -04:00
|
|
|
#[cfg(feature = "clone-with-caveats")]
|
|
|
|
impl Clone for How {
|
|
|
|
#[inline]
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
self.clone_without_backtrace()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clone_from(&mut self, source: &Self) {
|
|
|
|
self.0.location = source.0.location;
|
|
|
|
self.0.context.clone_from(&source.0.context);
|
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
{
|
|
|
|
self.0.backtrace = Backtrace::Disabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 01:51:08 -04:00
|
|
|
impl std::error::Error for How {}
|
2022-11-09 19:48:43 -05:00
|
|
|
|
|
|
|
impl std::fmt::Display for How {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2023-06-29 00:00:42 -04:00
|
|
|
let opts = ReportOpts::default();
|
|
|
|
for (i, ctx) in self.0.context.iter().enumerate() {
|
|
|
|
if i != 0 {
|
|
|
|
f.write_str("\n")?;
|
|
|
|
}
|
2022-11-09 19:48:43 -05:00
|
|
|
report_write!(f, &opts.indent().next(), "{ctx}")?;
|
|
|
|
}
|
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
{
|
2023-06-29 00:00:42 -04:00
|
|
|
use std::backtrace::BacktraceStatus::*;
|
|
|
|
let bt = &self.0.backtrace;
|
|
|
|
match bt {
|
|
|
|
Backtrace::Unsupported => f.write_str("\nI'd like to show you a backtrace,\n but it's not supported on your platform")?,
|
|
|
|
Backtrace::Disabled => f.write_str("\nIf you'd like a backtrace,\n try again with RUST_BACKTRACE=1")?,
|
|
|
|
Backtrace::Other(status, bt) => {
|
|
|
|
f.write_str(if *status == Captured {
|
|
|
|
"\nHere is the backtrace:"
|
|
|
|
} else {
|
|
|
|
"\nI can't tell if backtraces are working,\n but I'll give it a go:"
|
|
|
|
})?;
|
|
|
|
report_write!(f, &opts, "\n{}", bt)?;
|
|
|
|
}
|
|
|
|
}
|
2022-11-09 19:48:43 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2023-01-17 07:29:58 -05:00
|
|
|
|
|
|
|
impl std::fmt::Debug for How {
|
|
|
|
#[inline(always)]
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
if f.alternate() {
|
|
|
|
self.fmt_debug_alternate(f)
|
|
|
|
} else {
|
|
|
|
std::fmt::Display::fmt(self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|