borked 0.2.0

Simple and convienient error handling library for rust
Documentation
//! ## About Borked
//! Borked Provides functionality such that nearly all error types can be 
//! passed seemlessly through a `Result` of almost any type.
//!
//! This crate does simlar things to other crates, such as 
//! [failure](https://docs.rs/failure/0.1.8/failure/) or 
//! [error_chain](https://docs.rs/error-chain/0.12.4/error_chain/),
//! But although being lovely options for error handling, I was never quite 
//! satisfied with how they worked.
//!
//! Although this crate coud be used to coerse Errors implementing 
//! [BorkChain](trait.BorkChain.html) it is most useful when passing errors 
//! that implement the `BorkChain` Trait.  `Borkchain` will coerse any error 
//! that implements `std::error::Error` into a `BorkChain` compatable type.
//! There is also some provide methods for wrapping errors.
//!
//! ## Features
//! * [BorkChain](trait.BorkChain.html) Trait to allow for a single Rust error
//! type signature to work with any implementation of an error (within bounds)
//! * Easy chaining/wrapping of multiple errors with [BorkChain](trait.BorkChain.html)
//! * Easy creation of new Error Types: Errors need only implement [BorkChain](trait.BorkChain.html) Trait, and this crate takes care of all the other implementations 
//! needed ie. `std::error::Error`, `std::fmt::Display`, ...
//! * Inter-operability with `std::error::Error`
//! * Convienience macro [borked!](macro.borked.html)
//! * Transparently wrapping result values with [bork_with()](trait.BorkWith.html)
//! * Convienience type [Borkable](type.Borkable.html) for shorter Result types.
//!
//! ## Examples
//! ```
//! #[macro_use]
//! extern crate borked;
//! use borked::{BaseBork, BorkWith, BorkWithMsg, Borkable};
//! 
//! fn borked_base()-> Borkable<()>{
//!     // Do something useful.
//!     // Encounter error.
//!     borked!("this is borked");
//!     // Never reached in this case.
//!     Ok(())
//! }
//!
//! fn print_bork(){
//!     let e = borked_base().unwrap_err();
//!     let e_str = format!("{}", e);
//!     // Borks are automatically formatted to include 'Error: ' before name.
//!     assert!(e_str == "Error: this is borked");
//! }
//! 
//! fn borked_base_w_err()-> Borkable<()>{
//!     // Do something useful.
//!     // Encounter error.
//!     borked!(TY => BaseBork);
//!     // Never reached in this case.
//!     Ok(())
//! }
//! 
//! fn borked_std_err() -> Borkable<()>{
//!     // We can still use the '?' operator with std::error::Error's
//!     let e = u32::from_str_radix("ASDFA", 10)?;
//!     Ok(())
//! }
//! 
//! fn multiple_borked() -> Borkable<()>{
//!     let e0 = borked_base().unwrap_err();
//!     borked!("Borked Twice?", e0);
//!     Ok(())
//! }
//!
//! fn print_multi_bork(){
//!     let e = multile_borked().unwrap_err();
//!     let e_str = format!("{}", e);
//!     // Borks are automatically formatted to display their causes.
//!     assert!(e_str == "Error: Borked Twice? => Cause: [Error: this is borked]");
//! }
//!
//! fn multi_borked_q() ->Borkable<()>{
//!     // the '?' operator can also obviously be used with Bork errors.
//!     multiple_borked()?;
//!     Ok(())
//! }
//! 
//! fn bork_with() -> Borkable<()>{
//!     // Here if an error is encountered it is wrapped with a bork with the
//!     // BorkWith trait.
//!     let i = u32::from_str_radix("ASDFA", 10)
//!     .bork_with(BaseBork::msg("This is a message!"))?;
//!     Ok(())
//! }
//! ``` 

use std::error::Error;
use std::convert::From;
use core::any::TypeId;

#[doc(hidden)]
pub mod macros;

/// Shortcut result type.
pub type Borkable<T> = Result<T, Box<dyn BorkChain>>;

/// Trait to Wrap Errors with BorkChain Errors.  This is implemented by this
/// crate for most types that would concievably need it, 
/// and should not need to be implemented explicitly in most cases.
pub trait BorkWith<T>{
    /// Translates a result from arbitrary type, to a result of type BorkChain,
    /// where the original error (if any) is set as the cause of the BorkChain.
    fn bork_with(self, e: Box<dyn BorkChain>) -> Result<T, Box<dyn BorkChain>>;

}

/// Trait for borks that implement a fn to make a new Bork with message dirrectly.
pub trait BorkWithMsg{
    /// This should return a new instantation of the type implementing it, 
    /// and the string passed in as msg should in some way be returned by the
    /// [name](trait.BorkChain.html#tymethod.name) function in [BorkChain](trait.BorkChain.html).
    fn msg(m: &'static str) -> Box<Self> where Self: Sized + BorkChain;
}

/// Trait for borks to be instantiated with no message
pub trait BorkNoMsg{
    /// This should return a new instantation of the type implementing it.
    fn no_msg() -> Box<Self> where Self: Sized + BorkChain;
}

/// Main Bork Trait. See [BaseBork](struct.BaseBork.html) for an example implementation.
pub trait BorkChain: 'static + Send + Sync{
    /// Used to store Bork within the Bork referenced by self.
    /// The stored Error is considered to be the cause of the referenced Error.
    /// The stored error can then be retrieved with the cause() fn.
    fn chain(&mut self, cause: Box<dyn BorkChain>)-> &dyn BorkChain;

    /// Get the name of the Error. This will be used to print the error.
    fn name(&self) -> String;

    /// Get the cause of the error.  This is set either at Instantiation of the
    /// Error, or through the chain fn.
    fn cause(&self) -> Option<&'static dyn BorkChain>;

    /// Return a new Instantiation.  Return type is an option, so None can
    /// be returned if it is otherwise inconvienient to implement this.
    /// The other traits [BorkWithMsg](trait.BorkWithMsg.html) and/or 
    /// [BorkNoMsg](trait.BorkNoMsg.html) can be implemented instead for a method that
    /// is _not_ Optional.
    fn new() -> Option<Box<Self>> where Self: Sized + BorkChain;

    /// Used for downcasting Borks to their underlying type.
    fn __private_get_type_id__(&self) -> TypeId{
        TypeId::of::<Self>()
    }
}
impl std::fmt::Debug for dyn BorkChain {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "BorkChain (name: {name}, cause: {cause:?})",
               name = self.name(), 
               cause = self.cause())
    }
}

impl std::fmt::Display for dyn BorkChain{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let cause_str = match self.cause(){
            Some(c) => format!(" => Cause: [{}]", c),
            None => "".to_string(),
        };
        write!(f, "Error: {name}{cause_str}", 
               name = self.name(), 
               cause_str = cause_str)
    }
}

impl Error for dyn BorkChain{}

/// Impl for converting std::Error's to borks.
impl <E: Error + 'static + Send + Sync> From<E> for Box<dyn BorkChain>{
    fn from(e: E)-> Self{
        return Box::new(e);
    }
}

/*/// Impl for converting std::Error's to borks.
impl <E: Error + 'static> From<E> for &dyn BorkChain{
    fn from(e: E)-> Self{
        let dyn_ref: &'static dyn BorkChain = unsafe{
                    &*(&e as *const dyn BorkChain)
        };
        return dyn_ref;
    }
}
*/
/// For wrapping Result<_, Std::Err>
impl <T, E: Error + 'static + Send + Sync> BorkWith<T> for Result<T, E> where Box<dyn BorkChain>: From<E> {
    fn bork_with(self, mut e: Box<dyn BorkChain>) -> Result<T, Box<dyn BorkChain>>{
        match self{
            Ok(v) => return Ok(v),
            Err(prev_err) => {
                e.chain(prev_err.into());
                return Err(e);
            },
        }
    }

}


/// For wrapping other Borks
impl <T> BorkWith<T> for Result<T, Box<dyn BorkChain>>{
    fn bork_with(self, mut e: Box<dyn BorkChain>) -> Result<T, Box<dyn BorkChain>>{
        match self{
            Ok(v) => return Ok(v),

            Err(prev_err) => {
                e.chain(prev_err);
                return Err(e);
            },
        }
    }
}

/// Allows Box<Bork> to be treated as Bork
impl BorkChain for Box<dyn BorkChain>{
    fn chain(&mut self, cause: Box<dyn BorkChain>)-> &dyn BorkChain{
        return (**self).chain(cause);
    }
    fn name(&self) -> String{
        return (**self).name();
    }
    fn new() -> Option<Box<Self>>{
        return None;
    }
    fn cause(&self) -> Option<&'static dyn BorkChain>{
        return (**self).cause();
    }

}


/// Bork Traits for std:Error
impl<E: Error + 'static + Send + Sync> BorkChain for E{
    fn chain(&mut self, _cause: Box<dyn BorkChain>)-> &dyn BorkChain{
        return self;
    }
    fn name(&self) -> String{
        return format!("{}", self);
    }
    fn new() -> Option<Box<Self>>{
        return None;
    }
    fn cause(&self) -> Option<&'static dyn BorkChain>{None}

}

/// Downcasting fn for BorkChain.  Allows the original type of bork to be reconstructed.
impl dyn BorkChain{
    /// This can be used to cast a BorkChain reference back to the concrete type 
    /// that implements BorkChain.
    pub fn downcast_ref<T: BorkChain>(&self) ->Option<&T>{
        if self.__private_get_type_id__() == TypeId::of::<T>(){
            return unsafe{
                Some(&*(self as *const dyn BorkChain as *const T))
            };
        }
        else{
            return None;
        }
    }
}

/// Basic bork implemntation.  Used by borked! macro, But could also be useful
/// for use as a basic error type. (or as a templeate for implementing custom
/// Bork errors.
#[derive(Debug)]
pub struct BaseBork{
    pub name: String,
    pub cause: Option<Box<dyn BorkChain>>,
}

impl std::error::Error for BaseBork{}
unsafe impl Send for BaseBork {}
unsafe impl Sync for BaseBork {}
impl std::fmt::Display for BaseBork {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.name)
    }
}
/*
impl BorkChain for BaseBork{
    fn chain(&mut self, cause: Box<dyn BorkChain>)-> &dyn BorkChain{
        self.cause = Some(cause);
        return self;
    }
    fn name(&self) -> String {self.name.clone()}
    fn cause(&self) -> Option<&'static dyn BorkChain> {
        return match &self.cause{
            Some(cause_ref) =>{
                let stat_ref: &'static dyn BorkChain = unsafe{
                    &*(cause_ref as *const dyn BorkChain)
                };
                return Some(stat_ref);
            },
            None => None,
        };
    }
    fn new() -> Option<Box<Self>> {
        Some(Box::new(Self {name: "Base Error".into(), cause: None}))}
}
*/


impl BorkWithMsg for BaseBork{
    fn msg(m: &'static str) -> Box<Self>{
        return Box::new(Self {name: m.into(), cause: None});
    }

}

impl BorkNoMsg for BaseBork{
    fn no_msg() -> Box<Self>{
        return Self::new().unwrap();
    }

}


//impl Error for BaseBork{}

#[cfg(test)]
mod tests;