quickjs_rusty/value/
object.rsuse std::fmt::Debug;
use std::ops::Deref;
use libquickjs_ng_sys as q;
use crate::utils::make_cstring;
use crate::{ExecutionError, ValueError};
use super::OwnedJsValue;
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedJsObject {
value: OwnedJsValue,
}
impl OwnedJsObject {
pub fn try_from_value(value: OwnedJsValue) -> Result<Self, ValueError> {
if !value.is_object() {
Err(ValueError::Internal("Expected an object".to_string()))
} else {
Ok(Self { value })
}
}
pub fn into_value(self) -> OwnedJsValue {
self.value
}
pub fn properties_iter(&self) -> Result<OwnedJsPropertyIterator, ValueError> {
let prop_iter = OwnedJsPropertyIterator::from_object(self.value.context(), self.clone())?;
Ok(prop_iter)
}
pub fn property(&self, name: &str) -> Result<Option<OwnedJsValue>, ExecutionError> {
let cname = make_cstring(name)?;
let value = {
let raw = unsafe {
q::JS_GetPropertyStr(self.value.context(), self.value.value, cname.as_ptr())
};
OwnedJsValue::new(self.value.context(), raw)
};
let tag = value.tag();
if tag.is_exception() {
Err(ExecutionError::Internal(format!(
"Exception while getting property '{}'",
name
)))
}
else {
Ok(Some(value))
}
}
pub fn property_require(&self, name: &str) -> Result<OwnedJsValue, ExecutionError> {
self.property(name)?
.ok_or_else(|| ExecutionError::Internal(format!("Property '{}' not found", name)))
}
pub fn is_promise(&self) -> Result<bool, ExecutionError> {
if let Some(p) = self.property("then")? {
if p.is_function() {
return Ok(true);
}
}
if let Some(p) = self.property("catch")? {
if p.is_function() {
return Ok(true);
}
}
Ok(false)
}
pub fn set_property(&self, name: &str, value: OwnedJsValue) -> Result<(), ExecutionError> {
let cname = make_cstring(name)?;
unsafe {
let ret = q::JS_SetPropertyStr(
self.value.context(),
self.value.value,
cname.as_ptr(),
value.value,
);
if ret < 0 {
Err(ExecutionError::Internal(
"Could not set property".to_string(),
))
} else {
std::mem::forget(value);
Ok(())
}
}
}
}
impl Deref for OwnedJsObject {
type Target = OwnedJsValue;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedJsPropertyIterator {
context: *mut q::JSContext,
object: OwnedJsObject,
properties: *mut q::JSPropertyEnum,
length: u32,
cur_index: u32,
}
impl OwnedJsPropertyIterator {
pub fn from_object(
context: *mut q::JSContext,
object: OwnedJsObject,
) -> Result<Self, ValueError> {
let mut properties: *mut q::JSPropertyEnum = std::ptr::null_mut();
let mut length: u32 = 0;
let flags = (q::JS_GPN_STRING_MASK | q::JS_GPN_SYMBOL_MASK | q::JS_GPN_ENUM_ONLY) as i32;
let ret = unsafe {
q::JS_GetOwnPropertyNames(
context,
&mut properties,
&mut length,
object.value.value,
flags,
)
};
if ret != 0 {
return Err(ValueError::Internal(
"Could not get object properties".into(),
));
}
Ok(Self {
context,
object,
properties,
length,
cur_index: 0,
})
}
}
impl Iterator for OwnedJsPropertyIterator {
type Item = Result<OwnedJsValue, ExecutionError>;
fn next(&mut self) -> Option<Self::Item> {
let cur_index = self.cur_index / 2;
let is_key = (self.cur_index % 2) == 0;
if cur_index >= self.length {
return None;
}
let prop = unsafe { self.properties.offset(cur_index as isize) };
let value = if is_key {
let pair_key = unsafe { q::JS_AtomToString(self.context, (*prop).atom) };
let tag = unsafe { q::JS_Ext_ValueGetTag(pair_key) };
if tag == q::JS_TAG_EXCEPTION {
return Some(Err(ExecutionError::Internal(
"Could not get object property name".into(),
)));
}
OwnedJsValue::new(self.context, pair_key)
} else {
let pair_value =
unsafe { q::JS_GetProperty(self.context, self.object.value.value, (*prop).atom) };
let tag = unsafe { q::JS_Ext_ValueGetTag(pair_value) };
if tag == q::JS_TAG_EXCEPTION {
return Some(Err(ExecutionError::Internal(
"Could not get object property".into(),
)));
}
OwnedJsValue::new(self.context, pair_value)
};
self.cur_index += 1;
Some(Ok(value))
}
}
impl Drop for OwnedJsPropertyIterator {
fn drop(&mut self) {
unsafe {
q::JS_FreePropertyEnum(self.context, self.properties, self.length);
}
}
}