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 bytes = std::slice::from_raw_parts(ptr as *const u8, len);
94
95    // Convert to String (validate UTF-8).
96    let s = String::from_utf8_lossy(bytes).into_owned();
97
98    // Free the c string.
99    q::JS_FreeCString(context, ptr);
100
101    Ok(s)
102}
103
104pub fn from_string_q(q_ctx: &QuickJsRealmAdapter, s: &str) -> Result<QuickJsValueAdapter, JsError> {
105    unsafe { from_string(q_ctx.context, s) }
106}
107/// # Safety
108/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
109pub unsafe fn from_string(
110    context: *mut q::JSContext,
111    s: &str,
112) -> Result<QuickJsValueAdapter, JsError> {
113    let qval = q::JS_NewStringLen(context, s.as_ptr() as *const c_char, s.len() as _);
114    let ret = QuickJsValueAdapter::new(context, qval, false, true, "primitives::from_string qval");
115    if ret.is_exception() {
116        return Err(JsError::new_str("Could not create string in runtime"));
117    }
118
119    Ok(ret)
120}
121
122#[cfg(test)]
123pub mod tests {
124    use crate::facades::tests::init_test_rt;
125    use crate::jsutils::Script;
126
127    #[tokio::test]
128    async fn test_emoji() {
129        let rt = init_test_rt();
130
131        let res = rt.eval(None, Script::new("testEmoji.js", "'hi'")).await;
132
133        match res {
134            Ok(fac) => {
135                assert_eq!(fac.get_str(), "hi");
136            }
137            Err(e) => {
138                panic!("script failed: {}", e);
139            }
140        }
141
142        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘'")).await;
143
144        match res {
145            Ok(fac) => {
146                assert_eq!(fac.get_str(), "๐Ÿ‘");
147            }
148            Err(e) => {
149                panic!("script failed: {}", e);
150            }
151        }
152
153        let res = rt.eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘'")).await;
154
155        match res {
156            Ok(fac) => {
157                assert_eq!(fac.get_str(), "pre๐Ÿ‘");
158            }
159            Err(e) => {
160                panic!("script failed: {}", e);
161            }
162        }
163
164        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘post'")).await;
165
166        match res {
167            Ok(fac) => {
168                assert_eq!(fac.get_str(), "๐Ÿ‘post");
169            }
170            Err(e) => {
171                panic!("script failed: {}", e);
172            }
173        }
174
175        let res = rt
176            .eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘post'"))
177            .await;
178
179        match res {
180            Ok(fac) => {
181                assert_eq!(fac.get_str(), "pre๐Ÿ‘post");
182            }
183            Err(e) => {
184                panic!("script failed: {}", e);
185            }
186        }
187
188        let res = rt
189            .eval(
190                None,
191                Script::new("testEmoji.js", "JSON.stringify({c: '๐Ÿ‘'})"),
192            )
193            .await;
194
195        match res {
196            Ok(fac) => {
197                assert_eq!(fac.get_str(), "{\"c\":\"๐Ÿ‘\"}");
198            }
199            Err(e) => {
200                panic!("script failed: {}", e);
201            }
202        }
203    }
204}