183 lines
5.0 KiB
Rust
183 lines
5.0 KiB
Rust
use std::borrow::Cow;
|
|
#[cfg(feature = "backtrace")]
|
|
use std::backtrace::BacktraceStatus;
|
|
use std::fmt;
|
|
use std::panic::Location;
|
|
|
|
/// 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);
|
|
|
|
impl fmt::Display for Context {
|
|
#[inline(always)]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(&self.0, f)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
|
|
pub(crate) enum ContextInner {
|
|
Elem(ContextElem),
|
|
Compound(Vec<ContextElem>),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
|
|
pub(crate) enum ContextElem {
|
|
Str(&'static str),
|
|
String(String),
|
|
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 {
|
|
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 ContextElem {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Str(s) => f.write_str(s),
|
|
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"),
|
|
#[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)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait IntoContext {
|
|
fn into_context(self) -> Context;
|
|
|
|
#[inline(always)]
|
|
fn with(self, other: impl IntoContext) -> Context
|
|
where
|
|
Self: Sized,
|
|
{
|
|
self.into_context().with(other)
|
|
}
|
|
}
|
|
|
|
impl IntoContext for Context {
|
|
#[inline(always)]
|
|
fn into_context(self) -> Context {
|
|
self
|
|
}
|
|
|
|
/// 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
|
|
}
|
|
},
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl IntoContext for String {
|
|
#[inline(always)]
|
|
fn into_context(self) -> Context {
|
|
Context(ContextInner::Elem(ContextElem::String(self)))
|
|
}
|
|
}
|
|
|
|
impl IntoContext for &'static str {
|
|
#[inline(always)]
|
|
fn into_context(self) -> Context {
|
|
Context(ContextInner::Elem(ContextElem::Str(self)))
|
|
}
|
|
}
|
|
|
|
impl IntoContext for Cow<'static, str> {
|
|
#[inline]
|
|
fn into_context(self) -> Context {
|
|
match self {
|
|
Cow::Borrowed(s) => s.into_context(),
|
|
Cow::Owned(s) => s.into_context(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoContext for &'a Location<'static> {
|
|
#[inline]
|
|
fn into_context(self) -> Context {
|
|
Location::into_context(*self)
|
|
}
|
|
}
|
|
|
|
impl IntoContext for Location<'static> {
|
|
#[inline]
|
|
fn into_context(self) -> Context {
|
|
Context(ContextInner::Elem(ContextElem::Location(self)))
|
|
}
|
|
}
|
|
|
|
impl<C, F> IntoContext for F
|
|
where
|
|
C: IntoContext,
|
|
F: FnOnce() -> C,
|
|
{
|
|
#[inline(always)]
|
|
fn into_context(self) -> Context {
|
|
self().into_context()
|
|
}
|
|
}
|