#![deny(
broken_intra_doc_links,
missing_debug_implementations,
nonstandard_style,
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts
)]
#![forbid(unsafe_code)]
#![warn(
deprecated_in_future,
missing_copy_implementations,
missing_docs,
unreachable_pub,
unused_import_braces,
unused_labels,
unused_lifetimes,
unused_qualifications,
unused_results
)]
mod trace;
#[cfg(not(feature = "failure"))]
use std::error::Error;
use std::{
convert::{AsMut, AsRef},
sync::atomic::{AtomicUsize, Ordering},
};
use derive_more as dm;
#[cfg(feature = "failure")]
use failure::Fail;
pub use self::trace::*;
pub static DEFAULT_FRAMES_CAPACITY: AtomicUsize = AtomicUsize::new(10);
#[derive(Clone, Debug, dm::Display)]
#[display(fmt = "{}", err)]
pub struct Traced<E> {
err: E,
trace: Trace,
}
impl<E> Traced<E> {
#[inline]
pub fn into_inner(self) -> E {
self.err
}
#[inline]
pub fn into_parts(self) -> (E, Trace) {
(self.err, self.trace)
}
#[inline]
pub fn from_parts(err: E, f: Frame) -> Self {
err.wrap_traced(f)
}
#[inline]
pub fn trace(&self) -> &Trace {
&self.trace
}
}
impl<E> From<(E, Frame)> for Traced<E> {
#[inline]
fn from((err, f): (E, Frame)) -> Self {
Self::from_parts(err, f)
}
}
impl<E> AsRef<E> for Traced<E> {
#[inline]
fn as_ref(&self) -> &E {
&self.err
}
}
impl<E> AsMut<E> for Traced<E> {
#[inline]
fn as_mut(&mut self) -> &mut E {
&mut self.err
}
}
#[cfg(not(feature = "failure"))]
impl<E: Error> Error for Traced<E> {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.err.source()
}
}
#[cfg(feature = "failure")]
impl<E: Fail> Fail for Traced<E> {
#[inline]
fn name(&self) -> Option<&str> {
self.err.name()
}
#[inline]
fn cause(&self) -> Option<&dyn Fail> {
self.err.cause()
}
#[inline]
fn backtrace(&self) -> Option<&failure::Backtrace> {
self.err.backtrace()
}
}
#[cfg(feature = "failure")]
#[allow(clippy::use_self)]
impl<E: Fail> From<Traced<E>> for Box<dyn Fail> {
#[inline]
fn from(err: Traced<E>) -> Self {
Box::new(err)
}
}
pub trait WrapTraced<E> {
fn wrap_traced(self, f: Frame) -> Traced<E>;
}
impl<E> WrapTraced<E> for E {
fn wrap_traced(self, f: Frame) -> Traced<Self> {
let mut trace = Trace::new(Vec::with_capacity(
DEFAULT_FRAMES_CAPACITY.load(Ordering::Relaxed),
));
trace.push(f);
Traced { err: self, trace }
}
}
impl<E> WrapTraced<E> for Traced<E> {
#[inline]
fn wrap_traced(mut self, f: Frame) -> Self {
self.trace.push(f);
self
}
}
#[inline]
pub fn map_from<F, T: From<F>>(e: Traced<F>) -> Traced<T> {
Traced {
err: T::from(e.err),
trace: e.trace,
}
}
#[macro_export]
macro_rules! new {
($e:expr) => {
$crate::WrapTraced::wrap_traced($e, $crate::new_frame!())
};
}
#[macro_export]
macro_rules! map_from_and_new {
($e:expr) => {
$crate::new!($crate::map_from($e))
};
}
#[macro_export]
macro_rules! wrap {
() => ($crate::wrap!(_ => _));
($from:ty) => ($crate::wrap!($from => _));
(=> $to:ty) => ($crate::wrap!(_ => $to));
($from:ty => $to:ty) => {
|err: $from| -> $crate::Traced<$to> {
$crate::new!(err)
}
};
}
#[macro_export]
macro_rules! map_from_and_wrap {
() => ($crate::map_from_and_wrap!(_ => _));
($from:ty) => ($crate::map_from_and_wrap!($from => _));
(=> $to:ty) => ($crate::map_from_and_wrap!(_ => $to));
($from:ty => $to:ty) => {
|err: $crate::Traced<$from>| -> $crate::Traced<$to> {
$crate::new!($crate::map_from::<$from, $to>(err))
}
};
}
#[macro_export]
macro_rules! from_and_wrap {
() => ($crate::from_and_wrap!(_ => _));
($from:ty) => ($crate::from_and_wrap!($from => _));
(=> $to:ty) => ($crate::from_and_wrap!(_ => $to));
($from:ty => $to:ty) => {
|err: $from| -> $crate::Traced<$to> {
$crate::map_from::<$from, $to>($crate::new!(err))
}
};
}
#[cfg(test)]
mod new_macro_spec {
use super::Traced;
#[test]
fn creates_new_trace_frame_on_initialization() {
let err = super::new!(());
assert_eq!(err.trace.len(), 1, "creates initial frame");
}
#[test]
fn fills_trace_on_subsequent_calls() {
let err = super::new!(());
let err = super::new!(err);
let err = super::new!(err);
let err: Traced<()> = super::new!(err);
assert_eq!(err.trace.len(), 4, "trace growths with each call");
}
}
#[cfg(test)]
mod map_from_and_new_macro_spec {
use super::Traced;
#[test]
fn fills_trace_on_subsequent_calls() {
let err = super::new!(());
let err = super::map_from_and_new!(err);
let err = super::map_from_and_new!(err);
let err: Traced<()> = super::map_from_and_new!(err);
assert_eq!(err.trace.len(), 4, "trace growths with each call");
}
#[test]
fn applies_required_from_implementation() {
let err = super::new!(4u8);
let err: Traced<u32> = super::map_from_and_new!(err);
assert!(!err.trace.is_empty(), "captures frames");
}
}
#[cfg(test)]
mod wrap_macro_spec {
use super::Traced;
#[test]
fn creates_new_trace_frame_on_initialization() {
let res: Result<(), ()> = Err(());
let err = res.map_err(super::wrap!()).unwrap_err();
assert_eq!(err.trace.len(), 1, "creates initial frame");
}
#[test]
fn fills_trace_on_subsequent_calls() {
let res: Result<(), ()> = Err(());
let res = res.map_err(super::wrap!());
let res = res.map_err(super::wrap!());
let res = res.map_err(super::wrap!(Traced<_>));
let err = res.map_err(super::wrap!(=> ())).unwrap_err();
assert_eq!(err.trace.len(), 4, "trace growths with each call");
}
}
#[cfg(test)]
mod map_from_and_wrap_macro_spec {
use super::Traced;
#[test]
fn fills_trace_on_subsequent_calls() {
let res: Result<(), ()> = Err(());
let res = res.map_err(super::wrap!());
let res = res.map_err(super::map_from_and_wrap!());
let res = res.map_err(super::map_from_and_wrap!());
let err = res.map_err(super::map_from_and_wrap!(=> ())).unwrap_err();
assert_eq!(err.trace.len(), 4, "trace growths with each call");
}
#[test]
fn applies_required_from_implementation() {
let res: Result<(), u8> = Err(54);
let res = res.map_err(super::wrap!());
let err: Traced<u64> =
res.map_err(super::map_from_and_wrap!()).unwrap_err();
assert!(!err.trace.is_empty(), "captures frames");
}
}
#[cfg(test)]
mod from_and_wrap_macro_spec {
use super::Traced;
#[test]
fn fills_trace_on_subsequent_calls() {
let res: Result<(), ()> = Err(());
let res = res.map_err(super::wrap!());
let res = res.map_err(super::from_and_wrap!());
let res = res.map_err(super::from_and_wrap!());
let err = res.map_err(super::from_and_wrap!(=> ())).unwrap_err();
assert_eq!(err.trace.len(), 4, "trace growths with each call");
}
#[test]
fn applies_required_from_implementation() {
let res: Result<(), u8> = Err(54);
let err: Traced<u64> =
res.map_err(super::from_and_wrap!()).unwrap_err();
assert!(!err.trace.is_empty(), "captures frames");
}
}