use std::{ffi::c_void, sync::Arc};
use anyhow::{Result, anyhow};
use crate::{js::{SmartJSValue, object::create_object, quickjs}, shared::{
PtrMagic, object::get_object, pxs_Runtime, var::{pxs_Var, pxs_VarObject}
}};
struct JSPXSContainer {
value: SmartJSValue
}
impl JSPXSContainer {
pub fn from_value(value: SmartJSValue) -> Self {
JSPXSContainer { value }
}
}
impl PtrMagic for JSPXSContainer {}
unsafe extern "C" fn js_deleter(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
let _ = JSPXSContainer::from_raw(ptr as *mut JSPXSContainer);
}
pub(super) fn js_into_pxs(value: &SmartJSValue) -> Result<pxs_Var> {
if value.is_int() {
Ok(pxs_Var::new_i64(value.as_i32()? as i64))
} else if value.is_float() {
Ok(pxs_Var::new_f64(
value.as_f64()?,
))
} else if value.is_bool() {
Ok(pxs_Var::new_bool(value.as_bool()))
} else if value.is_string() {
Ok(pxs_Var::new_string(value.as_string()?))
} else if value.is_array() {
let mut values = vec![];
let length = value.get_prop("length").as_i32()?;
for i in 0..length {
let val = value.get_prop_pos(i as u32);
values.push(js_into_pxs(&val)?);
}
Ok(pxs_Var::new_list_with(values))
} else if value.is_function() {
Ok(pxs_Var::new_function(
JSPXSContainer::from_value(value.clone()).into_raw() as *mut c_void,
Some(js_deleter),
))
} else if value.is_exception() {
Ok(pxs_Var::new_exception(value.get_error_exception().unwrap()))
} else if value.is_error() {
Ok(pxs_Var::new_exception(value.get_error_exception().unwrap()))
} else if value.is_undefined() || value.is_null() {
Ok(pxs_Var::new_null())
} else {
Ok(pxs_Var::new_object(pxs_VarObject::new_lang_only(JSPXSContainer::from_value(value.clone()).into_void()), Some(js_deleter)))
}
}
pub(super) fn pxs_into_js(context: *mut quickjs::JSContext, var: &pxs_Var) -> Result<SmartJSValue> {
match var.tag {
crate::shared::var::pxs_VarType::pxs_Int64 => Ok(SmartJSValue::new_i32(context, var.get_i64()? as i32)),
crate::shared::var::pxs_VarType::pxs_UInt64 => Ok(SmartJSValue::new_i32(context, var.get_u64()? as i32)),
crate::shared::var::pxs_VarType::pxs_String => Ok(SmartJSValue::new_string(context, var.get_string()?)),
crate::shared::var::pxs_VarType::pxs_Bool => Ok(SmartJSValue::new_bool(context, var.get_bool()?)),
crate::shared::var::pxs_VarType::pxs_Float64 => Ok(SmartJSValue::new_f64(context, var.get_f64()?)),
crate::shared::var::pxs_VarType::pxs_Null => Ok(SmartJSValue::new_null(context)),
crate::shared::var::pxs_VarType::pxs_Object => {
let container_ptr = var.get_object_ptr();
if container_ptr.is_null() {
return Err(anyhow!("Object pointer not found"));
}
let container = unsafe{JSPXSContainer::from_borrow_void(container_ptr)};
Ok(container.value.clone())
},
crate::shared::var::pxs_VarType::pxs_HostObject => {
let idx = var.get_host_idx();
let po = get_object(idx).unwrap();
let lang_ptr_is_null = po.lang_ptr.lock().unwrap().is_null();
if lang_ptr_is_null {
let obj: SmartJSValue = create_object(context, idx, Arc::clone(&po));
let boxed = Box::into_raw(Box::new(obj));
po.update_lang_ptr(boxed as *mut c_void);
po.update_pxs_free_method(js_deleter);
}
let lang_ptr = po.lang_ptr.lock().unwrap();
let smart_value = *lang_ptr as *const SmartJSValue;
let value = unsafe{ (&*smart_value).clone() };
Ok(value)
},
crate::shared::var::pxs_VarType::pxs_List => {
let arr = SmartJSValue::new_array(context);
let vars = &var.get_list().unwrap().vars;
for i in 0..vars.len() {
let mut value = pxs_into_js(context, &vars[i])?;
arr.set_prop_pos(i as u32, &mut value);
}
Ok(arr)
},
crate::shared::var::pxs_VarType::pxs_Function => {
let container_ptr = var.get_function().unwrap();
if container_ptr.is_null() {
return Err(anyhow!("Function pointer not found"));
}
let container = unsafe{JSPXSContainer::from_borrow_void(container_ptr)};
Ok(container.value.clone())
},
crate::shared::var::pxs_VarType::pxs_Factory => {
let factory = var.get_factory().unwrap();
let res = factory.call(pxs_Runtime::pxs_JavaScript);
pxs_into_js(context, &res)
},
crate::shared::var::pxs_VarType::pxs_Exception => {
let message = var.get_string().unwrap();
let error = SmartJSValue::new_exception(context, message, "Error".to_string());
unsafe {
Ok(SmartJSValue::new_owned(quickjs::JS_Throw(context, error.dupped_value()), context))
}
},
crate::shared::var::pxs_VarType::pxs_Map => {
let object = SmartJSValue::new_object(context);
let map = var.get_map().unwrap();
let keys = map.keys();
for k in keys {
let item = map.get_item(k);
if let Some(item) = item {
let js_key = pxs_into_js(context, k)?;
let mut js_val = pxs_into_js(context, item)?;
object.set_prop_value(&js_key, &mut js_val);
}
}
Ok(object)
},
}
}