use core::{ffi::CStr, ptr::null_mut};
use alloc::{
boxed::Box,
string::{String, ToString},
};
use rmquickjs_sys::{
JSCStringBuf, JSGCRef, JSObjectClassEnum_JS_CLASS_ARRAY,
JSObjectClassEnum_JS_CLASS_FLOAT32_ARRAY, JSObjectClassEnum_JS_CLASS_FLOAT64_ARRAY,
JSObjectClassEnum_JS_CLASS_INT8_ARRAY, JSObjectClassEnum_JS_CLASS_INT16_ARRAY,
JSObjectClassEnum_JS_CLASS_INT32_ARRAY, JSObjectClassEnum_JS_CLASS_TYPED_ARRAY,
JSObjectClassEnum_JS_CLASS_UINT8_ARRAY, JSObjectClassEnum_JS_CLASS_UINT16_ARRAY,
JSObjectClassEnum_JS_CLASS_UINT32_ARRAY, JSValue,
};
use crate::{Array, Context, Function, Object};
#[derive(Debug, Clone, Copy)]
pub struct Value(JSValue);
impl Value {
pub fn from_raw(value: JSValue) -> Self {
Self(value)
}
pub fn into_raw(self) -> JSValue {
self.0
}
pub fn null() -> Self {
Self(rmquickjs_sys::JS_NULL())
}
pub fn undefined() -> Self {
Self(rmquickjs_sys::JS_UNDEFINED())
}
pub fn exception() -> Self {
Self(rmquickjs_sys::JS_EXCEPTION())
}
pub const fn is_exception(&self) -> bool {
self.0 == rmquickjs_sys::JS_EXCEPTION()
}
pub const fn is_null(&self) -> bool {
self.0 == rmquickjs_sys::JS_NULL()
}
pub const fn is_undefined(&self) -> bool {
self.0 == rmquickjs_sys::JS_UNDEFINED()
}
pub const fn is_true(&self) -> bool {
self.0 == rmquickjs_sys::JS_TRUE()
}
pub const fn is_false(&self) -> bool {
self.0 == rmquickjs_sys::JS_FALSE()
}
pub fn is_bool(&self) -> bool {
rmquickjs_sys::JS_IsBool(self.0)
}
pub const fn is_int(&self) -> bool {
rmquickjs_sys::JS_IsInt(self.0)
}
pub const fn is_ptr(&self) -> bool {
rmquickjs_sys::JS_IsPtr(self.0)
}
pub fn is_number(&self, ctx: &Context) -> bool {
unsafe { rmquickjs_sys::JS_IsNumber(ctx.as_ptr(), self.0) == 1 }
}
pub fn is_string(&self, ctx: &Context) -> bool {
unsafe { rmquickjs_sys::JS_IsString(ctx.as_ptr(), self.0) == 1 }
}
pub fn is_function(&self, ctx: &Context) -> bool {
unsafe { rmquickjs_sys::JS_IsFunction(ctx.as_ptr(), self.0) == 1 }
}
pub fn is_object(&self, ctx: &Context) -> bool {
unsafe { rmquickjs_sys::JS_GetClassID(ctx.as_ptr(), self.0) != -1 }
}
#[allow(non_upper_case_globals)]
pub fn is_array(&self, ctx: &Context) -> bool {
unsafe {
let class_id = rmquickjs_sys::JS_GetClassID(ctx.as_ptr(), self.0);
if class_id == -1 {
return false;
}
matches!(
class_id as u32,
JSObjectClassEnum_JS_CLASS_ARRAY
| JSObjectClassEnum_JS_CLASS_INT8_ARRAY
| JSObjectClassEnum_JS_CLASS_INT16_ARRAY
| JSObjectClassEnum_JS_CLASS_INT32_ARRAY
| JSObjectClassEnum_JS_CLASS_UINT8_ARRAY
| JSObjectClassEnum_JS_CLASS_UINT16_ARRAY
| JSObjectClassEnum_JS_CLASS_UINT32_ARRAY
| JSObjectClassEnum_JS_CLASS_FLOAT32_ARRAY
| JSObjectClassEnum_JS_CLASS_FLOAT64_ARRAY
| JSObjectClassEnum_JS_CLASS_TYPED_ARRAY
)
}
}
pub fn is_error(&self, ctx: &Context) -> bool {
unsafe { rmquickjs_sys::JS_IsError(ctx.as_ptr(), self.0) == 1 }
}
pub fn to_number(&self, ctx: &Context) -> Option<f64> {
let mut result = 0.0;
unsafe {
if rmquickjs_sys::JS_ToNumber(ctx.as_ptr(), &mut result, self.0) == 0 {
Some(result)
} else {
None
}
}
}
pub fn to_i32(&self, ctx: &Context) -> Option<i32> {
let mut result = 0;
unsafe {
if rmquickjs_sys::JS_ToInt32(ctx.as_ptr(), &mut result, self.0) == 0 {
Some(result)
} else {
None
}
}
}
pub fn to_i32_sat(&self, ctx: &Context) -> Option<i32> {
let mut result = 0;
unsafe {
if rmquickjs_sys::JS_ToInt32Sat(ctx.as_ptr(), &mut result, self.0) == 0 {
Some(result)
} else {
None
}
}
}
pub fn to_u32(&self, ctx: &Context) -> Option<u32> {
let mut result = 0;
unsafe {
if rmquickjs_sys::JS_ToUint32(ctx.as_ptr(), &mut result, self.0) == 0 {
Some(result)
} else {
None
}
}
}
pub fn to_string(&self, ctx: &Context) -> String {
unsafe {
let mut buf = JSCStringBuf { buf: [0; 5] };
let mut len = 0;
let str = rmquickjs_sys::JS_ToCStringLen(ctx.as_ptr(), &mut len, self.0, &mut buf);
CStr::from_ptr(str).to_string_lossy().to_string()
}
}
pub fn to_object<'a>(&self, ctx: &'a Context) -> Option<Object<'a>> {
if self.is_object(ctx) {
unsafe {
let mut gc_ref = Box::new(JSGCRef {
val: self.0,
prev: null_mut(),
});
let slot = rmquickjs_sys::JS_AddGCRef(ctx.as_ptr(), gc_ref.as_mut());
*slot = self.0;
Some(Object::new(gc_ref, ctx))
}
} else {
None
}
}
pub fn to_array<'a>(&self, ctx: &'a Context) -> Option<Array<'a>> {
if self.is_array(ctx) {
unsafe {
let mut gc_ref = Box::new(JSGCRef {
val: self.0,
prev: null_mut(),
});
let slot = rmquickjs_sys::JS_AddGCRef(ctx.as_ptr(), gc_ref.as_mut());
*slot = self.0;
Some(Array::new(gc_ref, ctx))
}
} else {
None
}
}
pub fn to_function<'a>(&self, ctx: &'a Context) -> Option<Function<'a>> {
if self.is_function(ctx) {
unsafe {
let mut gc_ref = Box::new(JSGCRef {
val: self.0,
prev: null_mut(),
});
let slot = rmquickjs_sys::JS_AddGCRef(ctx.as_ptr(), gc_ref.as_mut());
*slot = self.0;
Some(Function::new(gc_ref, ctx))
}
} else {
None
}
}
}