quickjs_runtime/quickjs_utils/
primitives.rs

1use crate::jsutils::JsError;
2use crate::quickjsrealmadapter::QuickJsRealmAdapter;
3use crate::quickjsvalueadapter::QuickJsValueAdapter;
4use core::ptr;
5use libquickjs_sys as q;
6use std::os::raw::c_char;
7
8pub fn to_bool(value_ref: &QuickJsValueAdapter) -> Result<bool, JsError> {
9    if value_ref.is_bool() {
10        let r = value_ref.borrow_value();
11        let raw = unsafe { r.u.int32 };
12        let val: bool = raw > 0;
13        Ok(val)
14    } else {
15        Err(JsError::new_str("value is not a boolean"))
16    }
17}
18
19pub fn from_bool(b: bool) -> QuickJsValueAdapter {
20    let raw = unsafe { q::JS_NewBool(ptr::null_mut(), b) };
21    QuickJsValueAdapter::new_no_context(raw, "primitives::from_bool")
22}
23
24pub fn to_f64(value_ref: &QuickJsValueAdapter) -> Result<f64, JsError> {
25    if value_ref.is_f64() {
26        let r = value_ref.borrow_value();
27        let val = unsafe { r.u.float64 };
28        Ok(val)
29    } else {
30        Err(JsError::new_str("value was not a float64"))
31    }
32}
33
34pub fn from_f64(f: f64) -> QuickJsValueAdapter {
35    #[cfg(feature = "bellard")]
36    let raw = unsafe { q::JS_NewFloat64(ptr::null_mut(), f) };
37
38    #[cfg(feature = "quickjs-ng")]
39    let raw = unsafe { q::JS_NewNumber(ptr::null_mut(), f) };
40
41    QuickJsValueAdapter::new_no_context(raw, "primitives::from_f64")
42}
43
44pub fn to_i32(value_ref: &QuickJsValueAdapter) -> Result<i32, JsError> {
45    if value_ref.is_i32() {
46        let r = value_ref.borrow_value();
47        let val: i32 = unsafe { r.u.int32 };
48        Ok(val)
49    } else {
50        Err(JsError::new_str("val is not an int"))
51    }
52}
53
54pub fn from_i32(i: i32) -> QuickJsValueAdapter {
55    let raw = unsafe { q::JS_NewInt32(ptr::null_mut(), i) };
56    QuickJsValueAdapter::new_no_context(raw, "primitives::from_i32")
57}
58
59pub fn to_string_q(
60    q_ctx: &QuickJsRealmAdapter,
61    value_ref: &QuickJsValueAdapter,
62) -> Result<String, JsError> {
63    unsafe { to_string(q_ctx.context, value_ref) }
64}
65/// # Safety
66/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
67pub unsafe fn to_string(
68    context: *mut q::JSContext,
69    value_ref: &QuickJsValueAdapter,
70) -> Result<String, JsError> {
71    //log::trace!("primitives::to_string on {}", value_ref.borrow_value().tag);
72
73    assert!(value_ref.is_string());
74
75    let mut len = 0;
76
77    #[cfg(feature = "bellard")]
78    let ptr: *const c_char = q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), 0);
79    #[cfg(feature = "quickjs-ng")]
80    let ptr: *const c_char =
81        q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), false);
82
83    if len == 0 {
84        return Ok("".to_string());
85    }
86
87    if ptr.is_null() {
88        return Err(JsError::new_str(
89            "Could not convert string: got a null pointer",
90        ));
91    }
92
93    let cstr = std::ffi::CStr::from_ptr(ptr);
94
95    let s = cstr.to_string_lossy().into_owned();
96
97    // Free the c string.
98    q::JS_FreeCString(context, ptr);
99
100    Ok(s)
101}
102
103/// # Safety
104/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
105pub unsafe fn to_str(
106    context: *mut q::JSContext,
107    value_ref: &QuickJsValueAdapter,
108) -> Result<&str, JsError> {
109    //log::trace!("primitives::to_str on {}", value_ref.borrow_value().tag);
110
111    assert!(value_ref.is_string());
112
113    let mut len = 0;
114
115    #[cfg(feature = "bellard")]
116    let ptr: *const c_char = q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), 0);
117    #[cfg(feature = "quickjs-ng")]
118    let ptr: *const c_char =
119        q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), false);
120    // Free the c string.
121    q::JS_FreeCString(context, ptr);
122    // ptr should still be valid as long as value_ref lives
123
124    if len == 0 {
125        return Ok("");
126    }
127
128    if ptr.is_null() {
129        return Err(JsError::new_str(
130            "Could not convert string: got a null pointer",
131        ));
132    }
133
134    let cstr = std::ffi::CStr::from_ptr(ptr);
135    cstr.to_str()
136        .map_err(|e| JsError::new_string(format!("utf8 error: {e}")))
137
138    //let s = cstr.to_string_lossy();
139
140    //Ok(s.as_ref())
141}
142
143pub fn from_string_q(q_ctx: &QuickJsRealmAdapter, s: &str) -> Result<QuickJsValueAdapter, JsError> {
144    unsafe { from_string(q_ctx.context, s) }
145}
146/// # Safety
147/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
148pub unsafe fn from_string(
149    context: *mut q::JSContext,
150    s: &str,
151) -> Result<QuickJsValueAdapter, JsError> {
152    let qval = q::JS_NewStringLen(context, s.as_ptr() as *const c_char, s.len() as _);
153    let ret = QuickJsValueAdapter::new(context, qval, false, true, "primitives::from_string qval");
154    if ret.is_exception() {
155        return Err(JsError::new_str("Could not create string in runtime"));
156    }
157
158    Ok(ret)
159}
160
161#[cfg(test)]
162pub mod tests {
163
164    use crate::facades::tests::init_test_rt;
165    use crate::jsutils::Script;
166
167    #[tokio::test]
168    async fn test_emoji() {
169        let rt = init_test_rt();
170
171        let res = rt.eval(None, Script::new("testEmoji.js", "'hi'")).await;
172
173        match res {
174            Ok(fac) => {
175                assert_eq!(fac.get_str(), "hi");
176            }
177            Err(e) => {
178                panic!("script failed: {}", e);
179            }
180        }
181
182        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘'")).await;
183
184        match res {
185            Ok(fac) => {
186                assert_eq!(fac.get_str(), "๐Ÿ‘");
187            }
188            Err(e) => {
189                panic!("script failed: {}", e);
190            }
191        }
192
193        let res = rt.eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘'")).await;
194
195        match res {
196            Ok(fac) => {
197                assert_eq!(fac.get_str(), "pre๐Ÿ‘");
198            }
199            Err(e) => {
200                panic!("script failed: {}", e);
201            }
202        }
203
204        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘post'")).await;
205
206        match res {
207            Ok(fac) => {
208                assert_eq!(fac.get_str(), "๐Ÿ‘post");
209            }
210            Err(e) => {
211                panic!("script failed: {}", e);
212            }
213        }
214
215        let res = rt
216            .eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘post'"))
217            .await;
218
219        match res {
220            Ok(fac) => {
221                assert_eq!(fac.get_str(), "pre๐Ÿ‘post");
222            }
223            Err(e) => {
224                panic!("script failed: {}", e);
225            }
226        }
227
228        let res = rt
229            .eval(
230                None,
231                Script::new("testEmoji.js", "JSON.stringify({c: '๐Ÿ‘'})"),
232            )
233            .await;
234
235        match res {
236            Ok(fac) => {
237                assert_eq!(fac.get_str(), "{\"c\":\"๐Ÿ‘\"}");
238            }
239            Err(e) => {
240                panic!("script failed: {}", e);
241            }
242        }
243    }
244}