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), } #[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 IntoContext for F where C: IntoContext, F: FnOnce() -> C, { #[inline(always)] fn into_context(self) -> Context { self().into_context() } }