how.rs/src/context.rs

131 lines
3.2 KiB
Rust

use std::borrow::Cow;
use std::fmt;
/// Provides context furthering the explanation of *how* you got to an error.
#[derive(Debug, Clone)]
pub struct Context(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, Clone)]
enum ContextInner {
Elem(ContextElem),
Compound(Vec<ContextElem>),
}
#[derive(Debug, Clone)]
enum ContextElem {
Str(&'static str),
String(String),
}
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),
}
}
}
pub trait IntoContext {
fn into_context(self) -> Context;
#[inline(always)]
fn chain(self, other: impl IntoContext) -> Context
where
Self: Sized,
{
self.into_context().chain(other)
}
}
impl IntoContext for Context {
#[inline(always)]
fn into_context(self) -> Context {
self
}
/// Chains another piece of context that is equal from a hierarchical perspective.
#[inline]
fn chain(self, other: impl IntoContext) -> Self {
Context(ContextInner::Compound(match self.0 {
ContextInner::Compound(mut elems) => {
match other.into_context().0 {
ContextInner::Elem(elem) => elems.push(elem),
ContextInner::Compound(mut elems1) => elems.append(&mut elems1),
};
elems
}
ContextInner::Elem(elem) => {
match other.into_context().0 {
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<C, F> IntoContext for F
where
C: IntoContext,
F: FnOnce() -> C,
{
#[inline(always)]
fn into_context(self) -> Context {
self().into_context()
}
}