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;
|
use std::borrow::Cow;
|
||||||
|
#[cfg(feature = "backtrace")]
|
||||||
|
use std::backtrace::BacktraceStatus;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
|
|
||||||
/// Provides context furthering the explanation of *how* you got to an error.
|
/// 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);
|
pub struct Context(pub(crate) ContextInner);
|
||||||
|
|
||||||
impl fmt::Display for Context {
|
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 {
|
pub(crate) enum ContextInner {
|
||||||
Elem(ContextElem),
|
Elem(ContextElem),
|
||||||
Compound(Vec<ContextElem>),
|
Compound(Vec<ContextElem>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
|
||||||
pub(crate) enum ContextElem {
|
pub(crate) enum ContextElem {
|
||||||
Str(&'static str),
|
Str(&'static str),
|
||||||
String(String),
|
String(String),
|
||||||
Location(Location<'static>),
|
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 {
|
impl fmt::Display for ContextInner {
|
||||||
|
@ -51,6 +73,19 @@ impl fmt::Display for ContextElem {
|
||||||
Self::Str(s) => f.write_str(s),
|
Self::Str(s) => f.write_str(s),
|
||||||
Self::String(s) => f.write_str(s),
|
Self::String(s) => f.write_str(s),
|
||||||
Self::Location(l) => write!(f, "At {l}"),
|
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::*;
|
use crate::context::*;
|
||||||
let mut context = context.into_context();
|
let mut context = context.into_context();
|
||||||
let loc = ContextElem::Location(*Location::caller());
|
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 {
|
match context.0 {
|
||||||
ContextInner::Elem(elem) => {
|
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) => {
|
ContextInner::Compound(ref mut vec) => {
|
||||||
vec.insert(1, loc);
|
vec.insert(1, loc);
|
||||||
|
|
67
src/how.rs
67
src/how.rs
|
@ -1,6 +1,4 @@
|
||||||
use core::panic::Location;
|
use core::panic::Location;
|
||||||
#[cfg(feature = "backtrace")]
|
|
||||||
use std::backtrace::BacktraceStatus;
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
@ -11,26 +9,16 @@ use crate::report::report_write;
|
||||||
/// By default, does not implement [`Clone`] because [`std::backtrace::Backtrace`] does not
|
/// 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
|
/// 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`].
|
/// [`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>);
|
pub struct How(Box<HowInner>);
|
||||||
|
|
||||||
struct HowInner {
|
struct HowInner {
|
||||||
location: &'static Location<'static>,
|
location: &'static Location<'static>,
|
||||||
#[cfg(feature = "backtrace")]
|
|
||||||
backtrace: Backtrace,
|
|
||||||
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
|
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
|
||||||
// Probably move after `backtrace`)
|
// Probably move after `backtrace`)
|
||||||
context: Vec<Context>,
|
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 {
|
impl How {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
|
@ -40,29 +28,10 @@ impl How {
|
||||||
Self(Box::new(HowInner {
|
Self(Box::new(HowInner {
|
||||||
location,
|
location,
|
||||||
context: Vec::with_capacity(4),
|
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)
|
.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 {
|
pub fn location(&self) -> &'static Location {
|
||||||
self.0.location
|
self.0.location
|
||||||
}
|
}
|
||||||
|
@ -103,8 +72,6 @@ impl How {
|
||||||
let mut b = f.debug_struct(std::any::type_name::<Self>());
|
let mut b = f.debug_struct(std::any::type_name::<Self>());
|
||||||
b.field("location", &(&self.0.location));
|
b.field("location", &(&self.0.location));
|
||||||
b.field("context", &(&self.0.context));
|
b.field("context", &(&self.0.context));
|
||||||
#[cfg(feature = "backtrace")]
|
|
||||||
let b = b.field("backtrace", &self.0.backtrace);
|
|
||||||
b.finish()
|
b.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,19 +81,18 @@ impl How {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "clone-with-caveats")]
|
#[cfg(feature = "clone-with-caveats")]
|
||||||
impl Clone for How {
|
impl Clone for HowInner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
self.clone_without_backtrace()
|
Self {
|
||||||
|
location: self.location,
|
||||||
|
context: self.context.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_from(&mut self, source: &Self) {
|
fn clone_from(&mut self, source: &Self) {
|
||||||
self.0.location = source.0.location;
|
self.location = source.location;
|
||||||
self.0.context.clone_from(&source.0.context);
|
self.context.clone_from(&source.context);
|
||||||
#[cfg(feature = "backtrace")]
|
|
||||||
{
|
|
||||||
self.0.backtrace = Backtrace::Disabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,23 +107,6 @@ impl std::fmt::Display for How {
|
||||||
}
|
}
|
||||||
report_write!(f, &opts.indent().next(), "{ctx}")?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue