Add support for Option and move How definition into its own file
This commit is contained in:
parent
d3bd38ba13
commit
cbb80c9d3f
|
@ -10,6 +10,7 @@ pub trait Explain: Sealed {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E> Sealed for Result<T, E> where Result<T, E>: IntoResultHow {}
|
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>
|
impl<T, E> Explain for Result<T, E>
|
||||||
where
|
where
|
||||||
|
@ -19,6 +20,18 @@ where
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn context(self, context: impl IntoContext) -> Self::Output {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
21
src/into.rs
21
src/into.rs
|
@ -19,9 +19,13 @@ where
|
||||||
{
|
{
|
||||||
type T = T;
|
type T = T;
|
||||||
|
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
fn into_result_how(self) -> Result<Self::T, How> {
|
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
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
101
src/lib.rs
101
src/lib.rs
|
@ -8,7 +8,6 @@ mod context;
|
||||||
pub use context::{Context, IntoContext};
|
pub use context::{Context, IntoContext};
|
||||||
|
|
||||||
mod report;
|
mod report;
|
||||||
use report::report_write;
|
|
||||||
pub use report::{Report, ReportOpts};
|
pub use report::{Report, ReportOpts};
|
||||||
|
|
||||||
mod into;
|
mod into;
|
||||||
|
@ -22,101 +21,7 @@ mod termination;
|
||||||
#[cfg(feature = "termination")]
|
#[cfg(feature = "termination")]
|
||||||
pub use termination::TerminationResult;
|
pub use termination::TerminationResult;
|
||||||
|
|
||||||
|
mod how;
|
||||||
|
pub use how::How;
|
||||||
|
|
||||||
pub type Result<T, E = How> = std::result::Result<T, E>;
|
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue