how.rs/src/how.rs

168 lines
4.6 KiB
Rust

use core::panic::Location;
use crate::*;
use crate::report::report_write;
/// The error type.
///
/// 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(
any(feature = "arc-backtrace", feature = "clone-with-caveats"),
derive(Clone)
)]
pub struct How(Box<HowInner>);
struct HowInner {
location: &'static Location<'static>,
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
// Probably move after `backtrace`)
context: Vec<Context>,
}
impl How {
#[must_use]
#[inline(never)]
#[track_caller]
pub fn new(context: impl IntoContext) -> Self {
let location = Location::caller();
#[allow(unused_mut)]
let mut how = Self(Box::new(HowInner {
location,
context: Vec::with_capacity(4),
}))
.frame(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.
let o = self.0.context.iter().next();
if cfg!(debug_assertions) {
o.unwrap()
} else {
#[allow(unsafe_code)]
unsafe {
o.unwrap_unchecked()
}
}
}
#[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.
let o = self.0.context.iter().next_back();
if cfg!(debug_assertions) {
o.unwrap()
} else {
#[allow(unsafe_code)]
unsafe {
o.unwrap_unchecked()
}
}
}
#[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()
}
fn fmt_debug_alternate(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut b = f.debug_struct(std::any::type_name::<Self>());
b.field("location", &(&self.0.location));
b.field("context", &(&self.0.context));
b.finish()
}
#[inline]
pub(crate) fn push_context(&mut self, context: Context) {
self.0.context.push(context);
}
}
#[cfg(any(feature = "arc-backtrace", feature = "clone-with-caveats"))]
impl Clone for HowInner {
#[inline]
fn clone(&self) -> Self {
Self {
location: self.location,
context: self.context.clone(),
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.location = source.location;
self.context.clone_from(&source.context);
}
}
impl std::error::Error for How {}
impl std::fmt::Display for How {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let opts = ReportOpts::default();
for (i, ctx) in self.0.context.iter().enumerate() {
if i != 0 {
f.write_str("\n")?;
}
report_write!(f, &opts.indent().next(), "{ctx}")?;
}
Ok(())
}
}
impl std::fmt::Debug for How {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
self.fmt_debug_alternate(f)
} else {
std::fmt::Display::fmt(self, f)
}
}
}