1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
//! ## 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{
    /// 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> 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> BorkWith<T> for Result<T, 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> 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{}

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;