use std::os;
use std::ffi::CString;
use std::ops::{Deref, Range};
use std::panic;
use std::slice;
use emacs_module::{emacs_value, EmacsSubr};
use super::error::Result;
use super::{CallEnv, Env, Value};
use super::{FromLisp, IntoLisp};
pub trait Manage {
unsafe fn make_function<T: Into<Vec<u8>>>(
&self,
function: EmacsSubr,
arities: Range<usize>,
doc: T,
data: *mut os::raw::c_void,
) -> Result<Value<'_>>;
fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>>;
}
pub trait HandleInit {
fn handle_init<F>(self, f: F) -> os::raw::c_int
where
F: Fn(&Env) -> Result<Value<'_>> + panic::RefUnwindSafe;
}
pub trait HandleCall {
fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
where
F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
T: IntoLisp<'e>;
}
impl Manage for Env {
#[allow(unused_unsafe)]
unsafe fn make_function<T: Into<Vec<u8>>>(
&self,
function: EmacsSubr,
arities: Range<usize>,
doc: T,
data: *mut os::raw::c_void,
) -> Result<Value<'_>> {
raw_call_value!(
self,
make_function,
arities.start as isize,
arities.end as isize,
Some(function),
CString::new(doc)?.as_ptr(),
data
)
}
fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>> {
let symbol = self.intern(name)?;
call_lisp!(self, "fset", symbol, func)
}
}
impl HandleInit for Env {
fn handle_init<F>(self, f: F) -> os::raw::c_int
where
F: Fn(&Env) -> Result<Value<'_>> + panic::RefUnwindSafe,
{
let env = panic::AssertUnwindSafe(self);
let result = panic::catch_unwind(|| match env.define_errors().and_then(|_| f(&env)) {
Ok(_) => 0,
Err(e) => {
env.message(format!("Error during initialization: {:#?}", e))
.expect("Fail to message Emacs about error");
1
}
});
match result {
Ok(v) => v,
Err(e) => {
env.message(format!("Panic during initialization: {:#?}", e))
.expect("Fail to message Emacs about panic");
2
}
}
}
}
impl CallEnv {
#[doc(hidden)]
#[inline]
pub unsafe fn new(
env: Env,
nargs: isize,
args: *mut emacs_value,
) -> Self {
let nargs = nargs as usize;
Self { env, nargs, args }
}
#[doc(hidden)]
#[inline]
pub fn raw_args(&self) -> &[emacs_value] {
unsafe { slice::from_raw_parts(self.args, self.nargs) }
}
pub fn args(&self) -> Vec<Value<'_>> {
self.raw_args().iter().map(|v| unsafe { Value::new(*v, &self.env) }).collect()
}
#[inline]
pub fn get_arg(&self, i: usize) -> Value<'_> {
let args: &[emacs_value] = self.raw_args();
unsafe { Value::new(args[i], &self) }
}
#[inline]
pub fn parse_arg<'e, T: FromLisp<'e>>(&'e self, i: usize) -> Result<T> {
self.get_arg(i).into_rust()
}
}
impl HandleCall for CallEnv {
#[inline]
fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
where
F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
T: IntoLisp<'e>,
{
let env = panic::AssertUnwindSafe(self);
let result = panic::catch_unwind(|| unsafe {
let rust_result = f(&env);
let lisp_result = rust_result.and_then(|t| t.into_lisp(&env));
env.maybe_exit(lisp_result)
});
env.handle_panic(result)
}
}
impl Deref for CallEnv {
type Target = Env;
#[doc(hidden)]
#[inline(always)]
fn deref(&self) -> &Env {
&self.env
}
}