Move Backtrace to a context element, capture along with Location
This commit is contained in:
parent
ac3e26950a
commit
f416f66d5c
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
67
src/how.rs
67
src/how.rs
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue