quickjs_rusty/value/
object.rs1use std::fmt::Debug;
2use std::ops::Deref;
3
4use libquickjs_ng_sys as q;
5
6use crate::utils::make_cstring;
7use crate::{ExecutionError, ValueError};
8
9use super::OwnedJsValue;
10
11#[derive(Clone, Debug, PartialEq)]
14pub struct OwnedJsObject {
15 value: OwnedJsValue,
16}
17
18impl OwnedJsObject {
19 pub fn try_from_value(value: OwnedJsValue) -> Result<Self, ValueError> {
20 if !value.is_object() {
21 Err(ValueError::Internal("Expected an object".to_string()))
22 } else {
23 Ok(Self { value })
24 }
25 }
26
27 pub fn into_value(self) -> OwnedJsValue {
28 self.value
29 }
30
31 pub fn properties_iter(&self) -> Result<OwnedJsPropertyIterator, ValueError> {
32 let prop_iter = OwnedJsPropertyIterator::from_object(self.value.context(), self.clone())?;
33
34 Ok(prop_iter)
35 }
36
37 pub fn property(&self, name: &str) -> Result<Option<OwnedJsValue>, ExecutionError> {
38 let cname = make_cstring(name)?;
40 let value = {
41 let raw = unsafe {
42 q::JS_GetPropertyStr(self.value.context(), self.value.value, cname.as_ptr())
43 };
44 OwnedJsValue::new(self.value.context(), raw)
45 };
46 let tag = value.tag();
47
48 if tag.is_exception() {
49 Err(ExecutionError::Internal(format!(
50 "Exception while getting property '{}'",
51 name
52 )))
53 }
54 else {
58 Ok(Some(value))
59 }
60 }
61
62 pub fn property_require(&self, name: &str) -> Result<OwnedJsValue, ExecutionError> {
63 self.property(name)?
64 .ok_or_else(|| ExecutionError::Internal(format!("Property '{}' not found", name)))
65 }
66
67 pub fn is_promise(&self) -> Result<bool, ExecutionError> {
70 if let Some(p) = self.property("then")? {
71 if p.is_function() {
72 return Ok(true);
73 }
74 }
75 if let Some(p) = self.property("catch")? {
76 if p.is_function() {
77 return Ok(true);
78 }
79 }
80 Ok(false)
81 }
82
83 pub fn set_property(&self, name: &str, value: OwnedJsValue) -> Result<(), ExecutionError> {
84 let cname = make_cstring(name)?;
85 unsafe {
86 let ret = q::JS_SetPropertyStr(
92 self.value.context(),
93 self.value.value,
94 cname.as_ptr(),
95 value.value,
96 );
97
98 if ret < 0 {
99 Err(ExecutionError::Internal(
100 "Could not set property".to_string(),
101 ))
102 } else {
103 std::mem::forget(value);
105 Ok(())
106 }
107 }
108 }
109}
110
111impl Deref for OwnedJsObject {
112 type Target = OwnedJsValue;
113
114 fn deref(&self) -> &Self::Target {
115 &self.value
116 }
117}
118
119#[derive(Clone, Debug, PartialEq)]
120pub struct OwnedJsPropertyIterator {
121 context: *mut q::JSContext,
122 object: OwnedJsObject,
123 properties: *mut q::JSPropertyEnum,
124 length: u32,
125 cur_index: u32,
126}
127
128impl OwnedJsPropertyIterator {
129 pub fn from_object(
130 context: *mut q::JSContext,
131 object: OwnedJsObject,
132 ) -> Result<Self, ValueError> {
133 let mut properties: *mut q::JSPropertyEnum = std::ptr::null_mut();
134 let mut length: u32 = 0;
135
136 let flags = (q::JS_GPN_STRING_MASK | q::JS_GPN_SYMBOL_MASK | q::JS_GPN_ENUM_ONLY) as i32;
137 let ret = unsafe {
138 q::JS_GetOwnPropertyNames(
139 context,
140 &mut properties,
141 &mut length,
142 object.value.value,
143 flags,
144 )
145 };
146 if ret != 0 {
147 return Err(ValueError::Internal(
148 "Could not get object properties".into(),
149 ));
150 }
151
152 Ok(Self {
153 context,
154 object,
155 properties,
156 length,
157 cur_index: 0,
158 })
159 }
160}
161
162impl Iterator for OwnedJsPropertyIterator {
165 type Item = Result<OwnedJsValue, ExecutionError>;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 let cur_index = self.cur_index / 2;
169 let is_key = (self.cur_index % 2) == 0;
170
171 if cur_index >= self.length {
172 return None;
173 }
174
175 let prop = unsafe { self.properties.offset(cur_index as isize) };
176
177 let value = if is_key {
178 let pair_key = unsafe { q::JS_AtomToString(self.context, (*prop).atom) };
179 let tag = unsafe { q::JS_Ext_ValueGetTag(pair_key) };
180 if tag == q::JS_TAG_EXCEPTION {
181 return Some(Err(ExecutionError::Internal(
182 "Could not get object property name".into(),
183 )));
184 }
185
186 OwnedJsValue::new(self.context, pair_key)
187 } else {
188 let pair_value =
189 unsafe { q::JS_GetProperty(self.context, self.object.value.value, (*prop).atom) };
190 let tag = unsafe { q::JS_Ext_ValueGetTag(pair_value) };
191 if tag == q::JS_TAG_EXCEPTION {
192 return Some(Err(ExecutionError::Internal(
193 "Could not get object property".into(),
194 )));
195 }
196
197 OwnedJsValue::new(self.context, pair_value)
198 };
199
200 self.cur_index += 1;
201
202 Some(Ok(value))
203 }
204}
205
206impl Drop for OwnedJsPropertyIterator {
207 fn drop(&mut self) {
208 unsafe {
209 q::JS_FreePropertyEnum(self.context, self.properties, self.length);
210 }
211 }
212}