Expose Detail

- Add some accessor methods on `Context`
- Wrap some variants in private structs to prevent people from depending
  on them
- Add some #[inline]
This commit is contained in:
Michael Pfaff 2023-07-01 12:35:14 -04:00
parent 52bcd3c17b
commit db067003d8
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
5 changed files with 100 additions and 26 deletions

View File

@ -6,6 +6,7 @@ edition = "2021"
[features]
default = []
backtrace = []
extra-backtrace = ["backtrace"]
clone-with-caveats = []
termination = ["dep:ansee"]

View File

@ -20,6 +20,26 @@ impl Context {
pub(crate) fn new(detail: Detail) -> Self {
Self { detail, extra: Vec::new() }
}
pub fn detail(&self) -> &Detail {
&self.detail
}
pub fn extra(&self) -> &[Detail] {
&self.extra
}
pub fn detail_mut(&mut self) -> &mut Detail {
&mut self.detail
}
pub fn extra_mut(&mut self) -> &mut [Detail] {
&mut self.extra
}
pub fn pop_extra(&mut self) -> Option<Detail> {
self.extra.pop()
}
}
impl fmt::Display for Context {
@ -37,15 +57,39 @@ impl fmt::Display for Context {
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum Detail {
#[non_exhaustive]
pub enum Detail {
Str(&'static str),
String(String),
Location(Location<'static>),
#[cfg(feature = "backtrace")]
Backtrace(Backtrace),
Error(Arc<dyn std::error::Error + Send + Sync>),
Backtrace(PrivateBacktrace),
Error(PrivateError),
}
impl Detail {
#[cfg(feature = "backtrace")]
#[track_caller]
pub fn backtrace() -> Self {
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()),
};
Self::Backtrace(PrivateBacktrace(bt))
}
}
#[derive(Debug, Clone)]
pub struct PrivateError(pub(crate) Arc<dyn std::error::Error + Send + Sync>);
#[cfg(feature = "backtrace")]
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub struct PrivateBacktrace(pub(crate) Backtrace);
// will be replaced with std::backtrace::Backtrace if and when it is Clone
#[cfg(feature = "backtrace")]
#[derive(Debug)]
@ -69,11 +113,11 @@ 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("I'd like to show you a backtrace,\n but it's not supported on your platform"),
Self::Backtrace(PrivateBacktrace(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("If you'd like a backtrace,\n try again with RUST_BACKTRACE=1"),
Self::Backtrace(PrivateBacktrace(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)) => {
Self::Backtrace(PrivateBacktrace(Backtrace::Other(status, bt))) => {
f.write_str(if *status == BacktraceStatus::Captured {
"Here is the backtrace:\n"
} else {
@ -81,7 +125,7 @@ impl fmt::Display for Detail {
})?;
write!(f, "{}", bt)
},
Self::Error(e) => e.fmt(f),
Self::Error(PrivateError(e)) => e.fmt(f),
}
}
}

View File

@ -2,8 +2,7 @@ use std::panic::Location;
use std::sync::Arc;
use crate::{How, IntoContext, Context, Detail};
#[cfg(feature = "backtrace")]
use crate::context::Backtrace;
use crate::context::PrivateError;
pub trait Explain {
type Output;
@ -21,23 +20,14 @@ impl Explain for How {
fn context(mut self, context: impl IntoContext) -> Self {
let mut context = context.into_context();
context.extra.reserve(if cfg!(feature = "backtrace") {
context.extra.reserve(if cfg!(feature = "extra-backtrace") {
2
} else {
1
});
context.extra.push(Detail::Location(*Location::caller()));
#[cfg(feature = "backtrace")]
context.extra.push({
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()),
};
Detail::Backtrace(bt)
});
#[cfg(feature = "extra-backtrace")]
context.extra.push(Detail::backtrace());
self.push_context(context);
self
}
@ -83,7 +73,7 @@ where
#[track_caller]
fn context(self, context: impl IntoContext) -> Self::Output {
How::new(Context::new(Detail::Error(self)))
How::new(Context::new(Detail::Error(PrivateError(self))))
.context(context)
}
}

View File

@ -25,17 +25,23 @@ impl How {
#[track_caller]
pub fn new(context: impl IntoContext) -> Self {
let location = Location::caller();
Self(Box::new(HowInner {
#[allow(unused_mut)]
let mut how = Self(Box::new(HowInner {
location,
context: Vec::with_capacity(4),
}))
.context(context)
.context(context);
#[cfg(all(feature = "backtrace", not(feature = "extra-backtrace")))]
how.top_mut().extra.push(Detail::backtrace());
how
}
#[inline]
pub fn location(&self) -> &'static Location {
self.0.location
}
#[inline]
pub fn top(&self) -> &Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context.
@ -50,6 +56,7 @@ impl How {
}
}
#[inline]
pub fn bottom(&self) -> &Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context.
@ -64,6 +71,37 @@ impl How {
}
}
#[inline]
pub fn top_mut(&mut self) -> &mut Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context.
let o = self.0.context.iter_mut().next();
if cfg!(debug_assertions) {
o.unwrap()
} else {
#[allow(unsafe_code)]
unsafe {
o.unwrap_unchecked()
}
}
}
#[inline]
pub fn bottom_mut(&mut self) -> &mut Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context.
let o = self.0.context.iter_mut().next_back();
if cfg!(debug_assertions) {
o.unwrap()
} else {
#[allow(unsafe_code)]
unsafe {
o.unwrap_unchecked()
}
}
}
#[inline]
pub fn into_context(self) -> impl Iterator<Item = Context> {
self.0.context.into_iter()
}
@ -75,6 +113,7 @@ impl How {
b.finish()
}
#[inline]
pub(crate) fn push_context(&mut self, context: Context) {
self.0.context.push(context);
}
@ -90,6 +129,7 @@ impl Clone for HowInner {
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.location = source.location;
self.context.clone_from(&source.context);

View File

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