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] [features]
default = [] default = []
backtrace = [] backtrace = []
extra-backtrace = ["backtrace"]
clone-with-caveats = [] clone-with-caveats = []
termination = ["dep:ansee"] termination = ["dep:ansee"]

View File

@ -20,6 +20,26 @@ impl Context {
pub(crate) fn new(detail: Detail) -> Self { pub(crate) fn new(detail: Detail) -> Self {
Self { detail, extra: Vec::new() } 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 { impl fmt::Display for Context {
@ -37,15 +57,39 @@ impl fmt::Display for Context {
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))] #[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
pub(crate) enum Detail { #[non_exhaustive]
pub enum Detail {
Str(&'static str), Str(&'static str),
String(String), String(String),
Location(Location<'static>), Location(Location<'static>),
#[cfg(feature = "backtrace")] #[cfg(feature = "backtrace")]
Backtrace(Backtrace), Backtrace(PrivateBacktrace),
Error(Arc<dyn std::error::Error + Send + Sync>), 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 // will be replaced with std::backtrace::Backtrace if and when it is Clone
#[cfg(feature = "backtrace")] #[cfg(feature = "backtrace")]
#[derive(Debug)] #[derive(Debug)]
@ -69,11 +113,11 @@ impl fmt::Display for Detail {
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")] #[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")] #[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")] #[cfg(feature = "backtrace")]
Self::Backtrace(Backtrace::Other(status, bt)) => { Self::Backtrace(PrivateBacktrace(Backtrace::Other(status, bt))) => {
f.write_str(if *status == BacktraceStatus::Captured { f.write_str(if *status == BacktraceStatus::Captured {
"Here is the backtrace:\n" "Here is the backtrace:\n"
} else { } else {
@ -81,7 +125,7 @@ impl fmt::Display for Detail {
})?; })?;
write!(f, "{}", bt) 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 std::sync::Arc;
use crate::{How, IntoContext, Context, Detail}; use crate::{How, IntoContext, Context, Detail};
#[cfg(feature = "backtrace")] use crate::context::PrivateError;
use crate::context::Backtrace;
pub trait Explain { pub trait Explain {
type Output; type Output;
@ -21,23 +20,14 @@ impl Explain for How {
fn context(mut self, context: impl IntoContext) -> Self { fn context(mut self, context: impl IntoContext) -> Self {
let mut context = context.into_context(); let mut context = context.into_context();
context.extra.reserve(if cfg!(feature = "backtrace") { context.extra.reserve(if cfg!(feature = "extra-backtrace") {
2 2
} else { } else {
1 1
}); });
context.extra.push(Detail::Location(*Location::caller())); context.extra.push(Detail::Location(*Location::caller()));
#[cfg(feature = "backtrace")] #[cfg(feature = "extra-backtrace")]
context.extra.push({ context.extra.push(Detail::backtrace());
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)
});
self.push_context(context); self.push_context(context);
self self
} }
@ -83,7 +73,7 @@ where
#[track_caller] #[track_caller]
fn context(self, context: impl IntoContext) -> Self::Output { 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) .context(context)
} }
} }

View File

@ -25,17 +25,23 @@ impl How {
#[track_caller] #[track_caller]
pub fn new(context: impl IntoContext) -> Self { pub fn new(context: impl IntoContext) -> Self {
let location = Location::caller(); let location = Location::caller();
Self(Box::new(HowInner { #[allow(unused_mut)]
let mut how = Self(Box::new(HowInner {
location, location,
context: Vec::with_capacity(4), 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 { pub fn location(&self) -> &'static Location {
self.0.location self.0.location
} }
#[inline]
pub fn top(&self) -> &Context { pub fn top(&self) -> &Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there // SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context. // is at least 1 value in context.
@ -50,6 +56,7 @@ impl How {
} }
} }
#[inline]
pub fn bottom(&self) -> &Context { pub fn bottom(&self) -> &Context {
// SAFETY: we only ever push values into context, and the constructor ensures that there // SAFETY: we only ever push values into context, and the constructor ensures that there
// is at least 1 value in context. // 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> { pub fn into_context(self) -> impl Iterator<Item = Context> {
self.0.context.into_iter() self.0.context.into_iter()
} }
@ -75,6 +113,7 @@ impl How {
b.finish() b.finish()
} }
#[inline]
pub(crate) fn push_context(&mut self, context: Context) { pub(crate) fn push_context(&mut self, context: Context) {
self.0.context.push(context); self.0.context.push(context);
} }
@ -90,6 +129,7 @@ impl Clone for HowInner {
} }
} }
#[inline]
fn clone_from(&mut self, source: &Self) { fn clone_from(&mut self, source: &Self) {
self.location = source.location; self.location = source.location;
self.context.clone_from(&source.context); self.context.clone_from(&source.context);

View File

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