use std::ffi::CString;
use std::ffi::c_int;
use std::ffi::c_void;
use std::ptr;
use std::str::FromStr;
use jimtcl_sys::Jim_CreateCommand;
use crate::JimResult;
use crate::error::JimError;
use crate::object::IntoJimObj;
use crate::sys;
use super::Interp;
use super::JimObject;
pub trait JimCommand {
fn invoke_cmd<'jim>(
&mut self,
interp: &'jim Interp,
args: &[JimObject<'jim>],
) -> JimResult<JimObject<'jim>>;
}
pub trait JimCommandFunc<'jim> {
fn invoke_func(
&mut self,
interp: &'jim Interp,
args: &[JimObject<'jim>],
) -> JimResult<JimObject<'jim>>;
}
#[repr(transparent)]
struct JimCmdData {
cmd: Box<dyn JimCommand>,
}
impl<'jim, PF, R> JimCommandFunc<'jim> for PF
where
PF: FnMut(&'jim Interp, &[JimObject<'jim>]) -> JimResult<R>,
R: IntoJimObj<'jim>,
{
fn invoke_func(
&mut self,
interp: &'jim Interp,
args: &[JimObject<'jim>],
) -> JimResult<JimObject<'jim>> {
Ok((self)(interp, args)?.to_jim(interp))
}
}
impl<PF> JimCommand for PF
where
PF: for<'jim> JimCommandFunc<'jim>,
{
fn invoke_cmd<'jim>(
&mut self,
interp: &'jim Interp,
args: &[JimObject<'jim>],
) -> JimResult<JimObject<'jim>> {
self.invoke_func(interp, args)
}
}
pub(crate) fn register_command<C: JimCommand + 'static>(
interp: &Interp,
name: &str,
cmd: C,
) -> JimResult<()> {
let name = CString::from_str(name)?;
let wrap = JimCmdData { cmd: Box::new(cmd) };
let data = Box::leak(Box::new(wrap));
let res = unsafe {
Jim_CreateCommand(
interp.interp,
name.as_ptr(),
Some(rust_jim_cmd),
ptr::from_mut(data) as *mut c_void,
Some(rust_free_jim),
)
};
if res == 0 {
Ok(())
} else {
Err(JimError::OtherCode((res as u32).into()))
}
}
unsafe extern "C" fn rust_jim_cmd(
interp: *mut sys::Jim_Interp,
argc: c_int,
argv: *const *mut sys::Jim_Obj,
) -> c_int {
let interp = Interp::wrap(interp);
let mut args = Vec::with_capacity(argc as usize);
for i in 0..argc {
unsafe {
args.push(JimObject::wrap(&interp, *argv.add(i as usize)));
}
}
let res = unsafe {
let data = (*interp.interp).cmdPrivData as *mut JimCmdData;
(*data).cmd.invoke_cmd(&interp, &args)
};
match res {
Err(JimError::OtherCode(c)) => c.into(),
Err(_) => sys::JIM_ERR as c_int,
Ok(mut v) => {
if !v.is_null() {
interp.set_result(&mut v);
}
0
}
}
}
unsafe extern "C" fn rust_free_jim(_interp: *mut sys::Jim_Interp, data: *mut c_void) {
let _ = unsafe {
let data = data as *mut JimCmdData;
Box::from_raw(data)
};
}