extern crate libc;
use std::error;
use std::fmt;
use std::panic::{self, RefUnwindSafe};
use std::result;
use std::path::Path;
use self::libc::c_char;
use chemfiles_sys::*;
use strings;
use Result;
#[derive(Clone, Debug, PartialEq)]
pub struct Error {
pub status: Status,
pub message: String,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Status {
Success,
CppStdError,
ChemfilesCppError,
MemoryError,
FileError,
FormatError,
SelectionError,
ConfigurationError,
OutOfBounds,
PropertyError,
UTF8PathError,
NullPtr,
}
impl From<chfl_status> for Error {
fn from(status: chfl_status) -> Error {
let status = match status {
chfl_status::CHFL_SUCCESS => Status::Success,
chfl_status::CHFL_CXX_ERROR => Status::CppStdError,
chfl_status::CHFL_GENERIC_ERROR => Status::ChemfilesCppError,
chfl_status::CHFL_MEMORY_ERROR => Status::MemoryError,
chfl_status::CHFL_FILE_ERROR => Status::FileError,
chfl_status::CHFL_FORMAT_ERROR => Status::FormatError,
chfl_status::CHFL_SELECTION_ERROR => Status::SelectionError,
chfl_status::CHFL_CONFIGURATION_ERROR => Status::ConfigurationError,
chfl_status::CHFL_OUT_OF_BOUNDS => Status::OutOfBounds,
chfl_status::CHFL_PROPERTY_ERROR => Status::PropertyError,
};
Error {
status: status,
message: Error::last_error(),
}
}
}
impl Error {
#[doc(hidden)]
pub fn utf8_path_error(path: &Path) -> Error {
Error {
status: Status::UTF8PathError,
message: format!("Could not convert '{}' to UTF8", path.display()),
}
}
#[doc(hidden)]
pub fn null_ptr() -> Error {
Error {
status: Status::NullPtr,
message: Error::last_error(),
}
}
pub fn last_error() -> String {
unsafe { strings::from_c(chfl_last_error()) }
}
pub fn cleanup() {
unsafe {
check(chfl_clear_errors()).expect("error in chfl_clear_errors. Things went very bad");
}
}
}
pub fn check(status: chfl_status) -> Result<()> {
if status != chfl_status::CHFL_SUCCESS {
return Err(Error::from(status));
}
return Ok(());
}
pub trait WarningCallback: RefUnwindSafe + Fn(&str) -> () {}
impl<T> WarningCallback for T
where
T: RefUnwindSafe + Fn(&str) -> (),
{
}
static mut LOGGING_CALLBACK: Option<*mut WarningCallback<Output = ()>> = None;
extern "C" fn warning_callback(message: *const c_char) {
unsafe {
let callback = &*LOGGING_CALLBACK.expect("No callback provided, this is an internal bug");
let _ = panic::catch_unwind(|| {
callback(&strings::from_c(message));
});
}
}
pub fn set_warning_callback<F>(callback: F) -> Result<()>
where
F: WarningCallback + 'static,
{
let callback = Box::into_raw(Box::new(callback));
unsafe {
if let Some(previous) = LOGGING_CALLBACK {
LOGGING_CALLBACK = Some(callback);
let _ = Box::from_raw(previous);
} else {
LOGGING_CALLBACK = Some(callback);
try!(check(chfl_set_warning_callback(warning_callback)));
}
}
return Ok(());
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
write!(fmt, "{}", self.message)
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match self.status {
Status::Success => "Success",
Status::CppStdError => "Exception from the C++ standard library",
Status::ChemfilesCppError => "Exception from the chemfiles library",
Status::MemoryError => "Error in memory allocations",
Status::FileError => "Error while reading or writing a file",
Status::FormatError => "Error in file formatting, i.e. the file is invalid",
Status::SelectionError => "Error in selection string syntax",
Status::UTF8PathError => "The given path is not valid UTF8",
Status::ConfigurationError => "Error in configuration files",
Status::OutOfBounds => "Out of bounds indexing",
Status::PropertyError => "Error in property",
Status::NullPtr => "We got a NULL pointer from C++",
}
}
}
#[cfg(test)]
mod test {
use super::*;
use Trajectory;
#[test]
fn errors() {
Error::cleanup();
assert_eq!(Error::last_error(), "");
assert!(Trajectory::open("nope", 'r').is_err());
assert_eq!(
Error::last_error(),
"file at \'nope\' does not have an extension, provide a format name to read it"
);
Error::cleanup();
assert_eq!(Error::last_error(), "");
}
#[test]
fn codes() {
assert_eq!(Error::from(chfl_status::CHFL_SUCCESS).status, Status::Success);
assert_eq!(Error::from(chfl_status::CHFL_CXX_ERROR).status, Status::CppStdError);
assert_eq!(Error::from(chfl_status::CHFL_GENERIC_ERROR).status, Status::ChemfilesCppError);
assert_eq!(Error::from(chfl_status::CHFL_MEMORY_ERROR).status, Status::MemoryError);
assert_eq!(Error::from(chfl_status::CHFL_FILE_ERROR).status, Status::FileError);
assert_eq!(Error::from(chfl_status::CHFL_FORMAT_ERROR).status, Status::FormatError);
assert_eq!(Error::from(chfl_status::CHFL_SELECTION_ERROR).status, Status::SelectionError);
assert_eq!(Error::from(chfl_status::CHFL_OUT_OF_BOUNDS).status, Status::OutOfBounds);
assert_eq!(Error::from(chfl_status::CHFL_PROPERTY_ERROR).status, Status::PropertyError);
}
}