use std::ffi::c_char;
use std::ffi::c_long;
use std::fmt;
use std::os::raw::c_double;
use std::os::raw::c_int;
use std::os::raw::c_longlong;
use std::ptr;
use std::ptr::null_mut;
use std::slice;
use std::str;
use crate::JimResult;
use crate::error::JimError;
use crate::interp::Interp;
use crate::sys;
pub trait IntoJimObj {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim>;
}
pub struct JimObject<'jim> {
pub(crate) interp: &'jim Interp,
obj: *mut sys::Jim_Obj,
}
impl<'jim> JimObject<'jim> {
pub(crate) fn wrap(interp: &'jim Interp, obj: *mut sys::Jim_Obj) -> JimObject<'jim> {
jim_incref(obj);
JimObject { interp, obj }
}
pub(crate) fn as_ref_ptr(&mut self) -> *mut sys::Jim_Obj {
jim_incref(self.obj);
self.obj
}
pub(crate) fn null(interp: &'jim Interp) -> JimObject<'jim> {
JimObject {
interp,
obj: null_mut(),
}
}
pub(crate) fn is_null(&self) -> bool {
self.obj.is_null()
}
#[allow(dead_code)]
pub(crate) fn non_null(&self) -> JimResult<Self> {
if self.is_null() {
Err(JimError::NullObject)
} else {
Ok(self.clone())
}
}
}
impl<'jim> fmt::Display for JimObject<'jim> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.obj.is_null() {
f.write_str("<NULL>")
} else {
unsafe {
let mut len: c_int = 0;
let ptr = sys::Jim_GetString(self.obj, &mut len) as *const u8;
let slice = slice::from_raw_parts(ptr, len as usize);
let repr = str::from_utf8(slice).expect("invalid string repr");
f.write_str(repr)
}
}
}
}
impl<'jim> fmt::Debug for JimObject<'jim> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<'jim> Clone for JimObject<'jim> {
fn clone(&self) -> Self {
unsafe {
(*self.obj).refCount += 1;
}
JimObject {
interp: self.interp,
obj: self.obj,
}
}
}
impl<'jim> Drop for JimObject<'jim> {
fn drop(&mut self) {
jim_decref(self.interp, &mut self.obj);
}
}
pub(crate) fn jim_incref(obj: *mut sys::Jim_Obj) {
if !obj.is_null() {
unsafe { (*obj).refCount += 1 }
}
}
pub(crate) fn jim_decref(interp: &Interp, obj: &mut *mut sys::Jim_Obj) {
if !obj.is_null() {
unsafe {
(**obj).refCount -= 1;
if (**obj).refCount <= 0 {
interp.free_obj(*obj);
*obj = null_mut();
}
}
}
}
impl IntoJimObj for () {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
JimObject::null(interp)
}
}
macro_rules! prim_int_jimcvt {
($type:ty) => {
impl IntoJimObj for $type {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
unsafe {
JimObject::wrap(
interp,
sys::Jim_NewIntObj(interp.interp, self as c_longlong),
)
}
}
}
impl<'jim> TryFrom<&JimObject<'jim>> for $type {
type Error = JimError;
fn try_from(jim: &JimObject<'jim>) -> Result<$type, Self::Error> {
let mut int: c_long = 0;
let rc = unsafe {
sys::Jim_GetLong(jim.interp.interp, jim.obj, ptr::from_mut(&mut int))
};
jim.interp.require_ok(rc as u32)?;
int.try_into()
.map_err(|_e| JimError::Error(format!("Jim integer out of bounds")))
}
}
};
}
prim_int_jimcvt!(u8);
prim_int_jimcvt!(i8);
prim_int_jimcvt!(u16);
prim_int_jimcvt!(i16);
prim_int_jimcvt!(u32);
prim_int_jimcvt!(i32);
prim_int_jimcvt!(u64);
prim_int_jimcvt!(i64);
prim_int_jimcvt!(usize);
prim_int_jimcvt!(isize);
macro_rules! prim_float_jimcvt {
($type:ty) => {
impl IntoJimObj for $type {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
unsafe {
JimObject::wrap(
interp,
sys::Jim_NewDoubleObj(interp.interp, self as c_double),
)
}
}
}
impl<'jim> TryFrom<&JimObject<'jim>> for $type {
type Error = JimError;
fn try_from(jim: &JimObject<'jim>) -> Result<$type, Self::Error> {
let mut val: c_double = 0.0;
let rc = unsafe {
sys::Jim_GetDouble(jim.interp.interp, jim.obj, ptr::from_mut(&mut val))
};
jim.interp.require_ok(rc as u32)?;
Ok(val as $type)
}
}
};
}
prim_float_jimcvt!(f32);
prim_float_jimcvt!(f64);
impl IntoJimObj for &str {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
unsafe {
JimObject::wrap(
interp,
sys::Jim_NewStringObjUtf8(
interp.interp,
self.as_ptr() as *const c_char,
self.len().try_into().expect("invalid string length"),
),
)
}
}
}
impl IntoJimObj for String {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
self.as_str().to_jim(interp)
}
}
impl<T: IntoJimObj> IntoJimObj for Option<T> {
fn to_jim<'jim>(self, interp: &'jim Interp) -> JimObject<'jim> {
match self {
Some(v) => v.to_jim(interp),
None => JimObject::null(interp),
}
}
}
#[test]
fn test_string_roundtrip() {
let interp = Interp::new().expect("failed to create Jim");
let msg = "HACKEM MUCHE";
let jim = msg.to_jim(&interp);
assert_eq!(&jim.to_string(), msg);
}
#[test]
fn test_int_roundtrip() {
let interp = Interp::new().expect("failed to create Jim");
let jim = 42u32.to_jim(&interp);
let out = u32::try_from(&jim).expect("failed to convert");
assert_eq!(out, 42);
}