Add support for Option and move How definition into its own file

This commit is contained in:
Michael Pfaff 2022-11-09 19:48:43 -05:00
parent d3bd38ba13
commit cbb80c9d3f
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
4 changed files with 137 additions and 101 deletions

View File

@ -10,6 +10,7 @@ pub trait Explain: Sealed {
}
impl<T, E> Sealed for Result<T, E> where Result<T, E>: IntoResultHow {}
impl<T> Sealed for Option<T> where Option<T>: IntoResultHow {}
impl<T, E> Explain for Result<T, E>
where
@ -19,6 +20,18 @@ where
#[inline(always)]
fn context(self, context: impl IntoContext) -> Self::Output {
self.into_result_how().map_err(|e| e.context(context))
self.into_result_how().map_err(#[inline(never)] move |e| e.context(context))
}
}
impl<T> Explain for Option<T>
where
Option<T>: IntoResultHow,
{
type Output = Result<<Self as IntoResultHow>::T, How>;
#[inline(always)]
fn context(self, context: impl IntoContext) -> Self::Output {
self.into_result_how().map_err(#[inline(never)] move |e| e.context(context))
}
}

101
src/how.rs Normal file
View File

@ -0,0 +1,101 @@
use crate::*;
use crate::report::report_write;
/// Does not implement [`std::error::Error`] to allow a [`From`] implementation for all other error types.
pub struct How(Box<HowInner>);
struct HowInner {
/// When true, the error will cause branchers to abort.
classified: bool,
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace,
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
// Probably move after `backtrace`)
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)]
pub fn new(context: impl IntoContext) -> Self {
Self(Box::new(HowInner {
classified: false,
context: {
let mut vec = Vec::with_capacity(4);
vec[0] = context.into_context();
vec
},
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace::capture(),
}))
}
#[must_use]
pub fn clone_without_backtrace(&self) -> Self {
Self(Box::new(HowInner {
classified: self.0.classified,
context: self.0.context.clone(),
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace::disabled(),
}))
}
#[inline]
#[must_use]
pub fn classified(mut self) -> Self {
self.0.classified = true;
self
}
#[inline]
#[must_use]
pub const fn is_classified(&self) -> bool {
self.0.classified
}
}
impl explain::Sealed for How {}
impl Explain for How {
type Output = Self;
#[inline(always)]
#[must_use]
fn context(mut self, context: impl IntoContext) -> Self {
self.0.context.push(context.into_context());
self
}
}
impl std::fmt::Display for How {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut opts = ReportOpts::default();
let mut ctxs = self.0.context.iter().rev();
let ctx = ctxs.next().expect("`How` created with no context.");
report_write!(f, &opts, "{ctx}")?;
for ctx in ctxs {
report_write!(f, &opts, "\n")?;
report_write!(f, &opts.indent().next(), "{ctx}")?;
opts = opts.indent();
}
#[cfg(feature = "backtrace")]
{
opts = opts.indent();
report_write!(f, &opts, "\n{}", self.0.backtrace)?;
}
Ok(())
}
}

View File

@ -19,9 +19,13 @@ where
{
type T = T;
#[inline]
#[inline(always)]
fn into_result_how(self) -> Result<Self::T, How> {
self.map_err(|e| How::new(e.to_string()))
#[inline(never)]
fn into<E: std::error::Error>(e: E) -> How {
How::new(e.to_string())
}
self.map_err(into)
}
}
@ -33,3 +37,16 @@ impl<T> IntoResultHow for Result<T, How> {
self
}
}
impl<T> IntoResultHow for Option<T> {
type T = T;
#[inline(always)]
fn into_result_how(self) -> Result<Self::T, How> {
#[inline(never)]
fn into() -> How {
How::new("None")
}
self.ok_or_else(into)
}
}

View File

@ -8,7 +8,6 @@ mod context;
pub use context::{Context, IntoContext};
mod report;
use report::report_write;
pub use report::{Report, ReportOpts};
mod into;
@ -22,101 +21,7 @@ mod termination;
#[cfg(feature = "termination")]
pub use termination::TerminationResult;
mod how;
pub use how::How;
pub type Result<T, E = How> = std::result::Result<T, E>;
/// Does not implement [`std::error::Error`] to allow a [`From`] implementation for all other error types.
pub struct How(Box<HowInner>);
struct HowInner {
/// When true, the error will cause branchers to abort.
classified: bool,
// TODO: consider storing this vec inline (sharing the allocation with rest of the struct.
// Probably move after `backtrace`)
context: Vec<Context>,
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace,
}
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]
pub fn new(context: impl IntoContext) -> Self {
Self(Box::new(HowInner {
classified: false,
context: {
let mut vec = Vec::with_capacity(2);
vec.push(context.into_context());
vec
},
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace::capture(),
}))
}
#[must_use]
pub fn clone_without_backtrace(&self) -> Self {
Self(Box::new(HowInner {
classified: self.0.classified,
context: self.0.context.clone(),
#[cfg(feature = "backtrace")]
backtrace: std::backtrace::Backtrace::disabled(),
}))
}
#[inline]
#[must_use]
pub fn classified(mut self) -> Self {
self.0.classified = true;
self
}
#[inline]
#[must_use]
pub const fn is_classified(&self) -> bool {
self.0.classified
}
}
impl explain::Sealed for How {}
impl Explain for How {
type Output = Self;
#[inline]
#[must_use]
fn context(mut self, context: impl IntoContext) -> Self {
self.0.context.push(context.into_context());
self
}
}
impl std::fmt::Display for How {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut opts = ReportOpts::default();
let mut ctxs = self.0.context.iter().rev();
let ctx = ctxs.next().expect("`How` created with no context.");
report_write!(f, &opts, "{ctx}")?;
for ctx in ctxs {
report_write!(f, &opts, "\n")?;
report_write!(f, &opts.indent().next(), "{ctx}")?;
opts = opts.indent();
}
#[cfg(feature = "backtrace")]
{
opts = opts.indent();
report_write!(f, &opts, "\n{}", self.0.backtrace)?;
}
Ok(())
}
}