Refactoring, improve output

This commit is contained in:
Michael Pfaff 2023-01-17 07:29:58 -05:00
parent 41e071bccf
commit bbc7df28d8
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
5 changed files with 89 additions and 39 deletions

View File

@ -1,19 +1,40 @@
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 std::fmt::Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
ContextInner::String(ref s) => f.write_str(s),
ContextInner::Compound(ref ctxs) => {
let mut ctxs = ctxs.iter();
if let Some(ctx) = ctxs.next() {
write!(f, "{ctx}")?;
for ctx in ctxs {
write!(f, "\n{ctx}")?;
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(())
@ -22,10 +43,13 @@ impl std::fmt::Display for Context {
}
}
#[derive(Debug, Clone)]
enum ContextInner {
String(Cow<'static, str>),
Compound(Vec<Context>),
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 {
@ -49,34 +73,48 @@ impl IntoContext for Context {
/// 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 {
Context(ContextInner::Compound(mut items)) => {
items.push(other.into_context());
items
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
}
_ => vec![self, other.into_context()],
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]
#[inline(always)]
fn into_context(self) -> Context {
Context(ContextInner::String(self.into()))
Context(ContextInner::Elem(ContextElem::String(self)))
}
}
impl IntoContext for &'static str {
#[inline]
#[inline(always)]
fn into_context(self) -> Context {
Context(ContextInner::String(self.into()))
Context(ContextInner::Elem(ContextElem::Str(self)))
}
}
impl IntoContext for Cow<'static, str> {
#[inline(always)]
#[inline]
fn into_context(self) -> Context {
Context(ContextInner::String(self))
match self {
Cow::Borrowed(s) => s.into_context(),
Cow::Owned(s) => s.into_context(),
}
}
}

View File

@ -32,6 +32,8 @@ where
#[inline(always)]
fn context(self, context: impl IntoContext) -> Self::Output {
// TODO: maybe add a toggle for the extra "Option::None" context
//Err(How::new(context))
self.into_result_how().map_err(#[inline(never)] move |e| e.context(context))
}
}
}

View File

@ -15,18 +15,6 @@ struct HowInner {
context: Vec<Context>,
}
impl std::fmt::Debug for How {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut b = f.debug_struct(std::any::type_name::<Self>());
let b = b
.field("classified", &self.0.classified)
.field("context", &(&self.0.context));
#[cfg(feature = "backtrace")]
let b = b.field("backtrace", &self.0.backtrace);
b.finish()
}
}
impl How {
#[must_use]
#[inline(never)]
@ -65,6 +53,16 @@ impl How {
pub const fn is_classified(&self) -> bool {
self.0.classified
}
fn fmt_debug_alternate(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut b = f.debug_struct(std::any::type_name::<Self>());
let b = b
.field("classified", &self.0.classified)
.field("context", &(&self.0.context));
#[cfg(feature = "backtrace")]
let b = b.field("backtrace", &self.0.backtrace);
b.finish()
}
}
impl explain::Sealed for How {}
@ -99,3 +97,14 @@ impl std::fmt::Display for How {
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)
}
}
}

View File

@ -45,7 +45,7 @@ impl<T> IntoResultHow for Option<T> {
fn into_result_how(self) -> Result<Self::T, How> {
#[inline(never)]
fn into() -> How {
How::new("None")
How::new("Option::None")
}
self.ok_or_else(into)
}

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")]
#![forbid(unsafe_code)]
#![feature(backtrace)]
mod sealed;
pub(crate) use sealed::seal;