use std;
use std::rc::Rc;
use std::io;
use std::ops::Deref;
use std::fmt;
use std::convert::From;
#[cfg(feature="cons_mode")]
use std::ffi::CString;
#[cfg(feature="cons_mode")]
use std::ffi::CStr;
#[cfg(feature="cons_mode")]
use std::ptr;
#[cfg(feature="cons_mode")]
use libc::*;
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum Error {
Cancel,
Other(Rc<OtherError>)
}
#[doc(hidden)]
#[derive(Debug)]
pub enum OtherError {
Retry(String),
Panic(String),
IO(io::Error)
}
#[doc(hidden)]
impl Error {
#[inline]
pub fn merge(&self, other: &Error) -> Error {
match (self, other) {
(&Error::Cancel, &Error::Cancel) => {
Error::Cancel
},
(&Error::Cancel, &Error::Other(ref y)) => {
match y.deref() {
&OtherError::Retry(_) => Error::Other(y.clone()),
_ => Error::Cancel
}
},
(&Error::Other(ref x), &Error::Cancel) => {
match x.deref() {
&OtherError::Retry(_) => Error::Other(x.clone()),
_ => Error::Cancel
}
},
(&Error::Other(ref x), &Error::Other(ref y)) => {
match (x.deref(), y.deref()) {
(&OtherError::Retry(_), _) => Error::Other(x.clone()),
(_, &OtherError::Retry(_)) => Error::Other(y.clone()),
(_, _) => Error::Other(x.clone())
}
}
}
}
#[inline]
pub fn retry(msg: String) -> Self {
Error::Other(OtherError::retry(msg))
}
#[inline]
pub fn panic(msg: String) -> Self {
Error::Other(OtherError::panic(msg))
}
#[inline]
pub fn io(err: io::Error) -> Self {
Error::Other(OtherError::io(err))
}
}
#[doc(hidden)]
impl OtherError {
#[inline]
pub fn retry(msg: String) -> Rc<Self> {
Rc::new(OtherError::Retry(msg))
}
#[inline]
pub fn panic(msg: String) -> Rc<Self> {
Rc::new(OtherError::Panic(msg))
}
#[inline]
pub fn io(err: io::Error) -> Rc<Self> {
Rc::new(OtherError::IO(err))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&Error::Cancel => write!(f, "Computation was canceled"),
&Error::Other(ref x) => x.fmt(f)
}
}
}
impl fmt::Display for OtherError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&OtherError::Retry(ref x) => write!(f, "{}", x),
&OtherError::Panic(ref x) => write!(f, "{}", x),
&OtherError::IO(ref x) => x.fmt(f)
}
}
}
impl std::error::Error for Error {}
impl std::error::Error for OtherError {}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::io(e)
}
}
#[cfg(feature="cons_mode")]
const CANCEL_STATUS: c_int = 1;
#[cfg(feature="cons_mode")]
const RETRY_STATUS: c_int = 2;
#[cfg(feature="cons_mode")]
const PANIC_STATUS: c_int = 3;
#[cfg(feature="cons_mode")]
const IO_ERROR_STATUS: c_int = 4;
#[cfg(feature="cons_mode")]
#[repr(C)]
pub struct ErrorRepr {
status: c_int,
message: *mut c_char,
delete_message: unsafe extern "C" fn(*mut c_char),
delete_error_repr: unsafe extern "C" fn(*mut ErrorRepr)
}
#[cfg(feature="cons_mode")]
impl Drop for ErrorRepr {
fn drop(&mut self) {
unsafe {
(self.delete_message)(self.message);
}
}
}
#[cfg(feature="cons_mode")]
impl ErrorRepr {
pub fn new(e: Error) -> Self {
let (status, message) = {
match e {
Error::Cancel => (CANCEL_STATUS, ptr::null_mut()),
Error::Other(x) => {
match x.deref() {
&OtherError::Retry(ref x) => {
let message = x.clone();
let message = CString::new(message).expect("NulError");
let message = CString::into_raw(message);
(RETRY_STATUS, message)
},
&OtherError::Panic(ref x) => {
let message = x.clone();
let message = CString::new(message).expect("NulError");
let message = CString::into_raw(message);
(PANIC_STATUS, message)
}
&OtherError::IO(ref x) => {
let message = format!("{}", x);
let message = CString::new(message).expect("NulError");
let message = CString::into_raw(message);
(IO_ERROR_STATUS, message)
}
}
}
}
};
ErrorRepr {
status: status,
message: message,
delete_message: delete_error_message,
delete_error_repr: delete_error_repr
}
}
}
#[cfg(feature="cons_mode")]
unsafe extern "C" fn delete_error_message(message: *mut c_char) {
if message != ptr::null_mut() {
let _ = CString::from_raw(message);
}
}
#[cfg(feature="cons_mode")]
unsafe extern "C" fn delete_error_repr(e: *mut ErrorRepr) {
if e != ptr::null_mut() {
let _ = Box::from_raw(e);
}
}
#[cfg(feature="cons_mode")]
pub unsafe fn ffi_error_repr_into_error(e: *mut ErrorRepr) -> Error {
if (*e).status == CANCEL_STATUS {
((*e).delete_error_repr)(e);
Error::Cancel
} else {
let message = CStr::from_ptr((*e).message);
let message = message.to_bytes().to_vec();
let message = String::from_utf8(message).expect("FromUtf8Error");
let status = (*e).status;
((*e).delete_error_repr)(e);
match status {
RETRY_STATUS => {
Error::Other(Rc::new(OtherError::Retry(message)))
},
PANIC_STATUS => {
Error::Other(Rc::new(OtherError::Panic(message)))
},
IO_ERROR_STATUS => {
let e = io::Error::new(io::ErrorKind::Other, message);
Error::Other(Rc::new(OtherError::IO(e)))
},
x => {
panic!("Unexpected error status: {}", x);
}
}
}
}