Move Backtrace to a context element, capture along with Location

This commit is contained in:
Michael Pfaff 2023-06-29 02:14:03 -04:00
parent ac3e26950a
commit f416f66d5c
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
3 changed files with 63 additions and 64 deletions

View File

@ -1,10 +1,12 @@
use std::borrow::Cow;
#[cfg(feature = "backtrace")]
use std::backtrace::BacktraceStatus;
use std::fmt;
use std::panic::Location;
/// Provides context furthering the explanation of *how* you got to an error.
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub struct Context(pub(crate) ContextInner);
impl fmt::Display for Context {
@ -14,17 +16,37 @@ impl fmt::Display for Context {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum ContextInner {
Elem(ContextElem),
Compound(Vec<ContextElem>),
}
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum ContextElem {
Str(&'static str),
String(String),
Location(Location<'static>),
#[cfg(feature = "backtrace")]
Backtrace(Backtrace),
}
// will be replaced with std::backtrace::Backtrace if and when it is Clone
#[cfg(feature = "backtrace")]
#[derive(Debug)]
pub(crate) enum Backtrace {
Disabled,
Unsupported,
Other(BacktraceStatus, String),
}
#[cfg(all(feature = "backtrace", feature = "clone-with-caveats"))]
impl Clone for Backtrace {
fn clone(&self) -> Self {
Backtrace::Disabled
}
}
impl fmt::Display for ContextInner {
@ -51,6 +73,19 @@ impl fmt::Display for ContextElem {
Self::Str(s) => f.write_str(s),
Self::String(s) => f.write_str(s),
Self::Location(l) => write!(f, "At {l}"),
#[cfg(feature = "backtrace")]
Self::Backtrace(Backtrace::Unsupported) => f.write_str("\nI'd like to show you a backtrace,\n but it's not supported on your platform"),
#[cfg(feature = "backtrace")]
Self::Backtrace(Backtrace::Disabled) => f.write_str("\nIf you'd like a backtrace,\n try again with RUST_BACKTRACE=1"),
#[cfg(feature = "backtrace")]
Self::Backtrace(Backtrace::Other(status, bt)) => {
f.write_str(if *status == BacktraceStatus::Captured {
"\nHere is the backtrace:\n"
} else {
"\nI can't tell if backtraces are working,\n but I'll give it a go:\n"
})?;
write!(f, "{}", bt)
},
}
}
}

View File

@ -19,9 +19,24 @@ impl Explain for How {
use crate::context::*;
let mut context = context.into_context();
let loc = ContextElem::Location(*Location::caller());
#[cfg(feature = "backtrace")]
let bt = {
use std::backtrace::BacktraceStatus::*;
let bt = std::backtrace::Backtrace::capture();
let bt = match bt.status() {
Disabled => Backtrace::Disabled,
Unsupported => Backtrace::Unsupported,
status => Backtrace::Other(status, bt.to_string()),
};
ContextElem::Backtrace(bt)
};
match context.0 {
ContextInner::Elem(elem) => {
context.0 = ContextInner::Compound(vec![elem, loc]);
#[cfg(not(feature = "backtrace"))]
let vec = vec![elem, loc];
#[cfg(feature = "backtrace")]
let vec = vec![elem, loc, bt];
context.0 = ContextInner::Compound(vec);
}
ContextInner::Compound(ref mut vec) => {
vec.insert(1, loc);

View File

@ -1,6 +1,4 @@
use core::panic::Location;
#[cfg(feature = "backtrace")]
use std::backtrace::BacktraceStatus;
use crate::*;
@ -11,26 +9,16 @@ use crate::report::report_write;
/// 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`].
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub struct How(Box<HowInner>);
struct HowInner {
location: &'static Location<'static>,
#[cfg(feature = "backtrace")]
backtrace: Backtrace,
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
// Probably move after `backtrace`)
context: Vec<Context>,
}
// 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),
}
impl How {
#[must_use]
#[inline(never)]
@ -40,29 +28,10 @@ impl How {
Self(Box::new(HowInner {
location,
context: Vec::with_capacity(4),
#[cfg(feature = "backtrace")]
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()),
}
},
}))
.context(context)
}
#[must_use]
pub fn clone_without_backtrace(&self) -> Self {
Self(Box::new(HowInner {
location: self.0.location,
context: self.0.context.clone(),
#[cfg(feature = "backtrace")]
backtrace: Backtrace::Disabled,
}))
}
pub fn location(&self) -> &'static Location {
self.0.location
}
@ -103,8 +72,6 @@ impl How {
let mut b = f.debug_struct(std::any::type_name::<Self>());
b.field("location", &(&self.0.location));
b.field("context", &(&self.0.context));
#[cfg(feature = "backtrace")]
let b = b.field("backtrace", &self.0.backtrace);
b.finish()
}
@ -114,19 +81,18 @@ impl How {
}
#[cfg(feature = "clone-with-caveats")]
impl Clone for How {
impl Clone for HowInner {
#[inline]
fn clone(&self) -> Self {
self.clone_without_backtrace()
Self {
location: self.location,
context: self.context.clone(),
}
}
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;
}
self.location = source.location;
self.context.clone_from(&source.context);
}
}
@ -141,23 +107,6 @@ impl std::fmt::Display for How {
}
report_write!(f, &opts.indent().next(), "{ctx}")?;
}
#[cfg(feature = "backtrace")]
{
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)?;
}
}
}
Ok(())
}
}