how.rs/src/context.rs

186 lines
5.1 KiB
Rust
Raw Normal View History

2022-07-21 20:39:47 -04:00
use std::borrow::Cow;
#[cfg(feature = "backtrace")]
use std::backtrace::BacktraceStatus;
2023-01-17 07:29:58 -05:00
use std::fmt;
use std::panic::Location;
2023-06-29 11:48:51 -04:00
use std::sync::Arc;
2023-01-17 07:29:58 -05:00
2022-07-21 20:40:44 -04:00
/// 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);
2022-07-21 20:39:47 -04:00
2023-01-17 07:29:58 -05:00
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 {
2023-06-29 11:55:21 -04:00
Elem(Detail),
Compound(Vec<Detail>),
2023-01-17 07:29:58 -05:00
}
#[derive(Debug)]
#[cfg_attr(feature = "clone-with-caveats", derive(Clone))]
2023-06-29 11:55:21 -04:00
pub(crate) enum Detail {
2023-01-17 07:29:58 -05:00
Str(&'static str),
String(String),
Location(Location<'static>),
#[cfg(feature = "backtrace")]
Backtrace(Backtrace),
2023-06-29 11:48:51 -04:00
Error(Arc<dyn std::error::Error + Send + Sync>),
}
// 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
}
2023-01-17 07:29:58 -05:00
}
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}")?;
2022-07-21 20:40:44 -04:00
}
2022-07-21 20:39:47 -04:00
}
2022-07-21 20:40:44 -04:00
Ok(())
2022-07-21 20:39:47 -04:00
}
}
}
}
2023-06-29 11:55:21 -04:00
impl fmt::Display for Detail {
2023-01-17 07:29:58 -05:00
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)
},
2023-06-29 11:48:51 -04:00
Self::Error(e) => e.fmt(f),
2023-01-17 07:29:58 -05:00
}
}
2022-07-21 20:39:47 -04:00
}
pub trait IntoContext {
fn into_context(self) -> Context;
2022-07-21 20:40:44 -04:00
#[inline(always)]
fn with(self, other: impl IntoContext) -> Context
2022-07-21 20:39:47 -04:00
where
Self: Sized,
{
self.into_context().with(other)
2022-07-21 20:39:47 -04:00
}
}
impl IntoContext for Context {
#[inline(always)]
fn into_context(self) -> Context {
self
}
2022-07-21 20:40:44 -04:00
/// Chains another piece of context that is a child from a hierarchical perspective.
#[track_caller]
2022-09-06 07:47:11 -04:00
#[inline]
fn with(self, other: impl IntoContext) -> Self {
let other = other.into_context().0;
2023-01-17 07:29:58 -05:00
Context(ContextInner::Compound(match self.0 {
ContextInner::Compound(mut elems) => {
match other {
2023-01-17 07:29:58 -05:00
ContextInner::Elem(elem) => elems.push(elem),
ContextInner::Compound(mut elems1) => elems.append(&mut elems1),
};
elems
2022-07-21 20:40:44 -04:00
}
ContextInner::Elem(elem) => match other {
ContextInner::Elem(elem1) => vec![elem, elem1],
ContextInner::Compound(mut elems) => {
elems.insert(0, elem);
elems
2023-01-17 07:29:58 -05:00
}
},
2022-09-06 07:47:11 -04:00
}))
2022-07-21 20:40:44 -04:00
}
2022-07-21 20:39:47 -04:00
}
impl IntoContext for String {
2023-01-17 07:29:58 -05:00
#[inline(always)]
2022-07-21 20:39:47 -04:00
fn into_context(self) -> Context {
2023-06-29 11:55:21 -04:00
Context(ContextInner::Elem(Detail::String(self)))
2022-07-21 20:39:47 -04:00
}
}
impl IntoContext for &'static str {
2023-01-17 07:29:58 -05:00
#[inline(always)]
2022-07-21 20:39:47 -04:00
fn into_context(self) -> Context {
2023-06-29 11:55:21 -04:00
Context(ContextInner::Elem(Detail::Str(self)))
2022-07-21 20:39:47 -04:00
}
}
impl IntoContext for Cow<'static, str> {
2023-01-17 07:29:58 -05:00
#[inline]
2022-07-21 20:39:47 -04:00
fn into_context(self) -> Context {
2023-01-17 07:29:58 -05:00
match self {
Cow::Borrowed(s) => s.into_context(),
Cow::Owned(s) => s.into_context(),
}
2022-07-21 20:39:47 -04:00
}
}
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 {
2023-06-29 11:55:21 -04:00
Context(ContextInner::Elem(Detail::Location(self)))
}
}
2022-07-21 20:39:47 -04:00
impl<C, F> IntoContext for F
where
C: IntoContext,
F: FnOnce() -> C,
{
#[inline(always)]
fn into_context(self) -> Context {
self().into_context()
}
}