Compare commits

...

3 Commits

3 changed files with 53 additions and 75 deletions

View File

@ -5,25 +5,36 @@ use std::fmt;
use std::panic::Location;
use std::sync::Arc;
use crate::ReportOpts;
use crate::report::report_write;
/// Provides context furthering the explanation of *how* you got to an error.
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub struct Context(pub(crate) ContextInner);
pub struct Context {
pub(crate) detail: Detail,
pub(crate) extra: Vec<Detail>,
}
impl Context {
pub(crate) fn new(detail: Detail) -> Self {
Self { detail, extra: Vec::new() }
}
}
impl fmt::Display for Context {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
self.detail.fmt(f)?;
let opts = ReportOpts::default();
for detail in &self.extra {
f.write_str("\n")?;
report_write!(f, &opts.indent().next(), "- {detail}")?;
}
Ok(())
}
}
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum ContextInner {
Elem(Detail),
Compound(Vec<Detail>),
}
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum Detail {
@ -51,24 +62,6 @@ impl Clone for Backtrace {
}
}
impl fmt::Display for ContextInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Elem(elem) => fmt::Display::fmt(elem, f),
Self::Compound(elems) => {
let mut elems = elems.iter();
if let Some(elem) = elems.next() {
fmt::Display::fmt(elem, f)?;
for elem in elems {
write!(f, "\n- {elem}")?;
}
}
Ok(())
}
}
}
}
impl fmt::Display for Detail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@ -76,15 +69,15 @@ impl fmt::Display for Detail {
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"),
Self::Backtrace(Backtrace::Unsupported) => f.write_str("I'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"),
Self::Backtrace(Backtrace::Disabled) => f.write_str("If 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"
"Here is the backtrace:\n"
} else {
"\nI can't tell if backtraces are working,\n but I'll give it a go:\n"
"I can't tell if backtraces are working,\n but I'll give it a go:\n"
})?;
write!(f, "{}", bt)
},
@ -93,16 +86,19 @@ impl fmt::Display for Detail {
}
}
pub trait IntoContext {
pub trait IntoContext: Sized {
fn into_context(self) -> Context;
#[inline(always)]
fn with(self, other: impl IntoContext) -> Context
where
Self: Sized,
{
fn with(self, other: impl IntoContext) -> Context {
self.into_context().with(other)
}
#[inline(always)]
#[track_caller]
fn with_caller(self) -> Context {
self.with(Location::caller())
}
}
impl IntoContext for Context {
@ -114,38 +110,26 @@ impl IntoContext for Context {
/// Chains another piece of context that is a child from a hierarchical perspective.
#[track_caller]
#[inline]
fn with(self, other: impl IntoContext) -> Self {
let other = other.into_context().0;
Context(ContextInner::Compound(match self.0 {
ContextInner::Compound(mut elems) => {
match other {
ContextInner::Elem(elem) => elems.push(elem),
ContextInner::Compound(mut elems1) => elems.append(&mut elems1),
};
elems
}
ContextInner::Elem(elem) => match other {
ContextInner::Elem(elem1) => vec![elem, elem1],
ContextInner::Compound(mut elems) => {
elems.insert(0, elem);
elems
}
},
}))
fn with(mut self, other: impl IntoContext) -> Self {
let mut other = other.into_context();
self.extra.reserve(1 + other.extra.len());
self.extra.push(other.detail);
self.extra.append(&mut other.extra);
self
}
}
impl IntoContext for String {
#[inline(always)]
fn into_context(self) -> Context {
Context(ContextInner::Elem(Detail::String(self)))
Context::new(Detail::String(self))
}
}
impl IntoContext for &'static str {
#[inline(always)]
fn into_context(self) -> Context {
Context(ContextInner::Elem(Detail::Str(self)))
Context::new(Detail::Str(self))
}
}
@ -169,7 +153,7 @@ impl<'a> IntoContext for &'a Location<'static> {
impl IntoContext for Location<'static> {
#[inline]
fn into_context(self) -> Context {
Context(ContextInner::Elem(Detail::Location(self)))
Context::new(Detail::Location(self))
}
}

View File

@ -1,7 +1,7 @@
use std::panic::Location;
use std::sync::Arc;
use crate::{How, IntoContext, Context, ContextInner, Detail};
use crate::{How, IntoContext, Context, Detail};
#[cfg(feature = "backtrace")]
use crate::context::Backtrace;
@ -20,9 +20,15 @@ impl Explain for How {
#[inline]
fn context(mut self, context: impl IntoContext) -> Self {
let mut context = context.into_context();
let loc = Detail::Location(*Location::caller());
context.extra.reserve(if cfg!(feature = "backtrace") {
2
} else {
1
});
context.extra.push(Detail::Location(*Location::caller()));
#[cfg(feature = "backtrace")]
let bt = {
context.extra.push({
use std::backtrace::BacktraceStatus::*;
let bt = std::backtrace::Backtrace::capture();
let bt = match bt.status() {
@ -31,19 +37,7 @@ impl Explain for How {
status => Backtrace::Other(status, bt.to_string()),
};
Detail::Backtrace(bt)
};
match context.0 {
ContextInner::Elem(elem) => {
#[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);
}
}
});
self.push_context(context);
self
}
@ -89,7 +83,7 @@ where
#[track_caller]
fn context(self, context: impl IntoContext) -> Self::Output {
How::new(Context(ContextInner::Elem(Detail::Error(self))))
How::new(Context::new(Detail::Error(self)))
.context(context)
}
}

View File

@ -3,7 +3,7 @@
#![feature(doc_auto_cfg)]
mod context;
pub(crate) use context::{ContextInner, Detail};
pub(crate) use context::Detail;
pub use context::{Context, IntoContext};
mod report;