#[doc(no_inline)]
pub use failure::Error;
use failure_derive::Fail;
use std::mem;
use std::result;
use std::thread;
use std::any::Any;
use super::IntoLisp;
use super::{Env, Value};
use emacs_module::*;
const RETURN: emacs_funcall_exit = emacs_funcall_exit_emacs_funcall_exit_return;
const SIGNAL: emacs_funcall_exit = emacs_funcall_exit_emacs_funcall_exit_signal;
const THROW: emacs_funcall_exit = emacs_funcall_exit_emacs_funcall_exit_throw;
#[derive(Debug)]
pub struct TempValue {
raw: emacs_value,
}
const WRONG_TYPE_USER_PTR: &str = "rust-wrong-type-user-ptr";
const ERROR: &str = "rust-error";
const PANIC: &str = "rust-panic";
#[derive(Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "Non-local signal: symbol={:?} data={:?}", symbol, data)]
Signal { symbol: TempValue, data: TempValue },
#[fail(display = "Non-local throw: tag={:?} value={:?}", tag, value)]
Throw { tag: TempValue, value: TempValue },
#[fail(display = "expected: {}", expected)]
WrongTypeUserPtr { expected: &'static str },
}
pub type Result<T> = result::Result<T, Error>;
impl TempValue {
unsafe fn new(raw: emacs_value) -> Self {
Self { raw }
}
pub unsafe fn value<'e>(&self, env: &'e Env) -> Value<'e> {
Value::new_protected(self.raw, env)
}
}
unsafe impl Send for TempValue {}
unsafe impl Sync for TempValue {}
impl Env {
#[inline]
pub(crate) fn handle_exit<T>(&self, result: T) -> Result<T> {
let mut symbol = unsafe { mem::uninitialized() };
let mut data = unsafe { mem::uninitialized() };
let status = self.non_local_exit_get(&mut symbol, &mut data);
match (status, symbol, data) {
(RETURN, ..) => Ok(result),
(SIGNAL, symbol, data) => {
self.non_local_exit_clear();
Err(ErrorKind::Signal {
symbol: unsafe { TempValue::new(symbol) },
data: unsafe { TempValue::new(data) },
}
.into())
}
(THROW, tag, value) => {
self.non_local_exit_clear();
Err(ErrorKind::Throw {
tag: unsafe { TempValue::new(tag) },
value: unsafe { TempValue::new(value) },
}
.into())
}
_ => panic!("Unexpected non local exit status {}", status),
}
}
#[inline]
pub(crate) unsafe fn maybe_exit(&self, result: Result<Value<'_>>) -> emacs_value {
match result {
Ok(v) => v.raw,
Err(error) => match error.downcast_ref::<ErrorKind>() {
Some(err) => self.handle_known(err),
_ => self
.signal_str(ERROR, &format!("{}", error))
.unwrap_or_else(|_| panic!("Failed to signal {}", error)),
},
}
}
#[inline]
pub(crate) fn handle_panic(&self, result: thread::Result<emacs_value>) -> emacs_value {
match result {
Ok(v) => v,
Err(error) => {
let mut m: result::Result<String, Box<Any>> = Err(error);
if let Err(error) = m {
m = error.downcast::<String>().map(|v| *v);
}
if let Err(error) = m {
m = match error.downcast::<ErrorKind>() {
Ok(err) => unsafe { return self.handle_known(&*err) },
Err(error) => Err(error),
}
}
if let Err(error) = m {
m = Ok(format!("{:#?}", error));
}
self.signal_str(PANIC, &m.expect("Logic error")).expect("Fail to signal panic")
}
}
}
pub(crate) fn define_errors(&self) -> Result<()> {
self.define_error(PANIC, "Rust panic", "error")?;
self.define_error(ERROR, "Rust error", "error")?;
self.define_error(WRONG_TYPE_USER_PTR, "Wrong type user-ptr", ERROR)?;
Ok(())
}
unsafe fn handle_known(&self, err: &ErrorKind) -> emacs_value {
match err {
&ErrorKind::Signal { ref symbol, ref data } => self.signal(symbol.raw, data.raw),
&ErrorKind::Throw { ref tag, ref value } => self.throw(tag.raw, value.raw),
&ErrorKind::WrongTypeUserPtr { .. } => self
.signal_str(WRONG_TYPE_USER_PTR, &format!("{}", err))
.unwrap_or_else(|_| panic!("Failed to signal {}", err)),
}
}
fn signal_str(&self, symbol: &str, message: &str) -> Result<emacs_value> {
let message = message.into_lisp(&self)?;
let data = self.list(&[message])?;
let symbol = self.intern(symbol)?;
unsafe { Ok(self.signal(symbol.raw, data.raw)) }
}
fn define_error(&self, name: &str, message: &str, parent: &str) -> Result<Value<'_>> {
self.call(
"define-error",
&[self.intern(name)?, message.into_lisp(self)?, self.intern(parent)?],
)
}
fn non_local_exit_get(
&self,
symbol: &mut emacs_value,
data: &mut emacs_value,
) -> emacs_funcall_exit {
raw_call_no_exit!(
self,
non_local_exit_get,
symbol as *mut emacs_value,
data as *mut emacs_value
)
}
fn non_local_exit_clear(&self) {
raw_call_no_exit!(self, non_local_exit_clear)
}
#[allow(unused_unsafe)]
unsafe fn throw(&self, tag: emacs_value, value: emacs_value) -> emacs_value {
raw_call_no_exit!(self, non_local_exit_throw, tag, value);
tag
}
#[allow(unused_unsafe)]
unsafe fn signal(&self, symbol: emacs_value, data: emacs_value) -> emacs_value {
raw_call_no_exit!(self, non_local_exit_signal, symbol, data);
symbol
}
}
pub trait ResultExt<T, E> {
fn unwrap_or_propagate(self) -> T;
}
impl<T> ResultExt<T, Error> for Result<T> {
#[inline]
fn unwrap_or_propagate(self) -> T {
self.unwrap_or_else(|error| {
match error.downcast::<ErrorKind>() {
Ok(err) => panic!(err),
Err(error) => panic!("{}", error),
};
})
}
}