use core::fmt::{self, Display, Debug};
use std::heap::{Heap, Alloc, Layout};
use core::mem;
use core::ptr;
use {Causes, Fail};
use backtrace::Backtrace;
use context::Context;
use compat::Compat;
pub struct Error {
inner: &'static mut Inner,
}
struct Inner {
backtrace: Backtrace,
vtable: *const VTable,
failure: FailData,
}
unsafe impl Send for Inner { }
unsafe impl Sync for Inner { }
extern {
type VTable;
type FailData;
}
struct InnerRaw<F> {
header: InnerHeader,
failure: F,
}
struct InnerHeader {
backtrace: Backtrace,
vtable: *const VTable,
}
struct TraitObject {
#[allow(dead_code)]
data: *const FailData,
vtable: *const VTable,
}
impl<F: Fail> From<F> for Error {
fn from(failure: F) -> Error {
let backtrace = if failure.backtrace().is_none() {
Backtrace::new()
} else {
Backtrace::none()
};
unsafe {
let vtable = mem::transmute::<_, TraitObject>(&failure as &Fail).vtable;
let ptr: *mut InnerRaw<F> = match Heap.alloc(Layout::new::<InnerRaw<F>>()) {
Ok(p) => p as *mut InnerRaw<F>,
Err(e) => Heap.oom(e),
};
ptr::write(ptr, InnerRaw {
header: InnerHeader {
backtrace,
vtable,
},
failure,
});
let inner: &'static mut Inner = mem::transmute(ptr);
Error { inner }
}
}
}
impl Inner {
fn failure(&self) -> &Fail {
unsafe {
mem::transmute::<TraitObject, &Fail>(TraitObject {
data: &self.failure as *const FailData,
vtable: self.vtable,
})
}
}
fn failure_mut(&mut self) -> &mut Fail {
unsafe {
mem::transmute::<TraitObject, &mut Fail>(TraitObject {
data: &mut self.failure as *const FailData,
vtable: self.vtable,
})
}
}
}
impl Error {
pub fn cause(&self) -> &Fail {
self.inner.failure()
}
pub fn backtrace(&self) -> &Backtrace {
self.inner.failure().backtrace().unwrap_or(&self.inner.backtrace)
}
pub fn context<D: Display + Send + Sync + 'static>(self, context: D) -> Context<D> {
Context::with_err(context, self)
}
pub fn compat(self) -> Compat<Error> {
Compat { error: self }
}
pub fn downcast<T: Fail>(self) -> Result<T, Error> {
let ret: Option<T> = self.downcast_ref().map(|fail| {
unsafe {
let _ = ptr::read(&self.inner.backtrace as *const Backtrace);
ptr::read(fail as *const T)
}
});
match ret {
Some(ret) => {
mem::forget(self);
Ok(ret)
}
_ => Err(self)
}
}
pub fn root_cause(&self) -> &Fail {
::find_root_cause(self.cause())
}
pub fn downcast_ref<T: Fail>(&self) -> Option<&T> {
self.inner.failure().downcast_ref()
}
pub fn downcast_mut<T: Fail>(&mut self) -> Option<&mut T> {
self.inner.failure_mut().downcast_mut()
}
pub fn causes(&self) -> Causes {
Causes { fail: Some(self.cause()) }
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.inner.failure(), f)
}
}
impl Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.inner.backtrace.is_none() {
Debug::fmt(self.inner.failure(), f)
} else {
write!(f, "{:?}\n\n{:?}", self.inner.failure(), self.inner.backtrace)
}
}
}
impl Drop for Error {
fn drop(&mut self) {
unsafe {
let layout = {
let header = Layout::new::<InnerHeader>();
header.extend(Layout::for_value(self.inner.failure())).unwrap().0
};
Heap.dealloc(self.inner as *const _ as *const u8 as *mut u8, layout);
}
}
}
#[cfg(test)]
mod test {
use std::mem::size_of;
use std::io;
use super::Error;
#[test]
fn assert_error_is_just_data() {
fn assert_just_data<T: Send + Sync + 'static>() { }
assert_just_data::<Error>();
}
#[test]
fn assert_is_one_word() {
assert_eq!(size_of::<Error>(), size_of::<usize>());
}
#[test]
fn methods_seem_to_work() {
let io_error: io::Error = io::Error::new(io::ErrorKind::NotFound, "test");
let error: Error = io::Error::new(io::ErrorKind::NotFound, "test").into();
assert!(error.downcast_ref::<io::Error>().is_some());
let _: ::Backtrace = *error.backtrace();
assert_eq!(format!("{:?}", io_error), format!("{:?}", error));
assert_eq!(format!("{}", io_error), format!("{}", error));
drop(error);
assert!(true);
}
}