Skip to main content

quickjs_rusty/value/
value.rs

1use std::collections::HashMap;
2use std::convert::TryFrom;
3use std::convert::TryInto;
4use std::hash::Hash;
5
6#[cfg(feature = "chrono")]
7use chrono::{DateTime, Utc};
8use libquickjs_ng_sys as q;
9
10#[cfg(feature = "bigint")]
11use crate::utils::create_bigint;
12#[cfg(feature = "chrono")]
13use crate::utils::create_date;
14use crate::utils::{
15    add_array_element, add_object_property, create_bool, create_empty_array, create_empty_object,
16    create_float, create_function, create_int, create_null, create_string,
17};
18use crate::OwnedJsPromise;
19use crate::{ExecutionError, ValueError};
20
21use super::tag::JsTag;
22use super::JsCompiledFunction;
23use super::JsFunction;
24use super::JsModule;
25use super::OwnedJsArray;
26use super::OwnedJsObject;
27
28/// OwnedJsValue wraps a Javascript value owned by the QuickJs runtime.
29///
30/// Guarantees cleanup of resources by dropping the value from the runtime.
31///
32/// **Safety**:
33///
34/// This type is `Send` and `Sync` only for convenience, since [OwnedJsValue](crate::OwnedJsValue)
35/// itself is just a wrapper around a raw pointer. But any operation on the underlying raw pointer is unsafe.
36/// Make sure using it in a same thread.
37///
38pub struct OwnedJsValue {
39    context: *mut q::JSContext,
40    // FIXME: make private again, just for testing
41    pub(crate) value: q::JSValue,
42}
43
44unsafe impl Send for OwnedJsValue {}
45unsafe impl Sync for OwnedJsValue {}
46
47impl PartialEq for OwnedJsValue {
48    fn eq(&self, other: &Self) -> bool {
49        unsafe { q::JS_Ext_GetPtr(self.value) == q::JS_Ext_GetPtr(other.value) }
50    }
51}
52
53impl OwnedJsValue {
54    #[inline]
55    pub fn context(&self) -> *mut q::JSContext {
56        self.context
57    }
58
59    /// Create a new `OwnedJsValue` from a `JsValue`.
60    /// This will **NOT** increase the ref count of the underlying value. So
61    /// you have to manage memory yourself. Be careful when using this.
62    #[inline]
63    pub fn new(context: *mut q::JSContext, value: q::JSValue) -> Self {
64        Self { context, value }
65    }
66
67    /// Create a new `OwnedJsValue` from a `JsValue`.
68    /// This will increase the ref count of the underlying value.
69    #[inline]
70    pub fn own(context: *mut q::JSContext, value: &q::JSValue) -> Self {
71        unsafe { q::JS_DupValue(context, *value) };
72        Self::new(context, *value)
73    }
74
75    #[inline]
76    pub fn tag(&self) -> JsTag {
77        JsTag::from_c(&self.value)
78    }
79
80    /// Get the inner JSValue without increasing ref count.
81    ///
82    /// Unsafe because the caller must ensure proper memory management.
83    pub unsafe fn as_inner(&self) -> &q::JSValue {
84        &self.value
85    }
86
87    /// Extract the underlying JSValue.
88    ///
89    /// Unsafe because the caller must ensure memory management. (eg JS_FreeValue)
90    pub unsafe fn extract(self) -> q::JSValue {
91        let v = self.value;
92        std::mem::forget(self);
93        v
94    }
95
96    /// Replace the underlying JSValue.
97    /// This will decrease the ref count of the old value but remain the ref count of the new value.
98    pub fn replace(&mut self, new: q::JSValue) {
99        unsafe {
100            q::JS_FreeValue(self.context, self.value);
101        }
102        self.value = new;
103    }
104
105    /// Check if this value is `null`.
106    #[inline]
107    pub fn is_null(&self) -> bool {
108        self.tag().is_null()
109    }
110
111    /// Check if this value is `undefined`.
112    #[inline]
113    pub fn is_undefined(&self) -> bool {
114        self.tag() == JsTag::Undefined
115    }
116
117    /// Check if this value is `bool`.
118    #[inline]
119    pub fn is_bool(&self) -> bool {
120        self.tag() == JsTag::Bool
121    }
122
123    /// Check if this value is `int`.
124    #[inline]
125    pub fn is_int(&self) -> bool {
126        self.tag() == JsTag::Int
127    }
128
129    /// Check if this value is `BigInt`.
130    #[inline]
131    #[cfg(feature = "bigint")]
132    pub fn is_bigint(&self) -> bool {
133        self.tag() == JsTag::BigInt || self.tag() == JsTag::ShortBigInt
134    }
135
136    /// Check if this value is `BigInt`, but short enough to fit in a i32
137    #[inline]
138    #[cfg(feature = "bigint")]
139    pub fn is_short_bigint(&self) -> bool {
140        self.tag() == JsTag::ShortBigInt
141    }
142
143    /// Check if this value is `float`.
144    #[inline]
145    pub fn is_float(&self) -> bool {
146        self.tag() == JsTag::Float64
147    }
148
149    /// Check if this value is a Javascript exception.
150    #[inline]
151    pub fn is_exception(&self) -> bool {
152        self.tag() == JsTag::Exception
153    }
154
155    /// Check if this value is a Javascript object.
156    #[inline]
157    pub fn is_object(&self) -> bool {
158        self.tag() == JsTag::Object
159    }
160
161    /// Check if this value is a Javascript array.
162    #[inline]
163    pub fn is_array(&self) -> bool {
164        unsafe { q::JS_IsArray(self.value) }
165    }
166
167    /// Check if this value is a Javascript array.
168    #[inline]
169    pub fn is_array_buffer(&self) -> bool {
170        unsafe { q::JS_IsArrayBuffer(self.value) }
171    }
172
173    /// Check if this value is a Javascript Proxy object.
174    #[inline]
175    pub fn is_proxy(&self) -> bool {
176        unsafe { q::JS_IsProxy(self.value) }
177    }
178
179    /// Check if this value is a Javascript function.
180    #[inline]
181    pub fn is_function(&self) -> bool {
182        unsafe { q::JS_IsFunction(self.context, self.value) }
183    }
184
185    /// Check if this value is a Javascript function.
186    #[inline]
187    pub fn is_constructor(&self) -> bool {
188        unsafe { q::JS_IsConstructor(self.context, self.value) }
189    }
190
191    /// Check if this value is a Javascript promise.
192    #[inline]
193    pub fn is_promise(&self) -> bool {
194        unsafe { q::JS_Ext_IsPromise(self.value) }
195    }
196
197    /// Check if this value is a Javascript error.
198    #[inline]
199    pub fn is_error(&self) -> bool {
200        unsafe { q::JS_IsError(self.value) }
201    }
202
203    /// Check if this value is a Javascript RegExp.
204    #[inline]
205    pub fn is_regexp(&self) -> bool {
206        unsafe { q::JS_IsRegExp(self.value) }
207    }
208
209    /// Check if this value is a Javascript Set.
210    #[inline]
211    pub fn is_set(&self) -> bool {
212        unsafe { q::JS_IsSet(self.value) }
213    }
214
215    /// Check if this value is a Javascript Map.
216    #[inline]
217    pub fn is_map(&self) -> bool {
218        unsafe { q::JS_IsMap(self.value) }
219    }
220
221    /// Check if this value is a Javascript WeakSet.
222    #[inline]
223    pub fn is_weak_set(&self) -> bool {
224        unsafe { q::JS_IsWeakSet(self.value) }
225    }
226
227    /// Check if this value is a Javascript WeakMap.
228    #[inline]
229    pub fn is_weak_map(&self) -> bool {
230        unsafe { q::JS_IsWeakMap(self.value) }
231    }
232
233    /// Check if this value is a Javascript WeakRef.
234    #[inline]
235    pub fn is_weak_ref(&self) -> bool {
236        unsafe { q::JS_IsWeakRef(self.value) }
237    }
238
239    /// Check if this value is a Javascript DataView.
240    #[inline]
241    pub fn is_data_view(&self) -> bool {
242        unsafe { q::JS_IsDataView(self.value) }
243    }
244
245    /// Check if this value is a Javascript module.
246    #[inline]
247    pub fn is_module(&self) -> bool {
248        self.tag().is_module()
249    }
250
251    /// Check if this value is a Javascript string.
252    #[inline]
253    pub fn is_string(&self) -> bool {
254        unsafe { q::JS_Ext_IsString(self.value) }
255    }
256
257    /// Check if this value is a bytecode compiled function.
258    #[inline]
259    pub fn is_compiled_function(&self) -> bool {
260        self.tag() == JsTag::FunctionBytecode
261    }
262
263    #[inline]
264    fn check_tag(&self, expected: JsTag) -> Result<(), ValueError> {
265        if self.tag() == expected {
266            Ok(())
267        } else {
268            Err(ValueError::UnexpectedType)
269        }
270    }
271
272    /// Convert this value into a bool
273    pub fn to_bool(&self) -> Result<bool, ValueError> {
274        self.check_tag(JsTag::Bool)?;
275        let val = unsafe { q::JS_Ext_GetBool(self.value) };
276        Ok(val == 1)
277    }
278
279    /// Convert this value into an i32
280    pub fn to_int(&self) -> Result<i32, ValueError> {
281        self.check_tag(JsTag::Int)?;
282        let val = unsafe { q::JS_Ext_GetInt(self.value) };
283        Ok(val)
284    }
285
286    /// Convert this value into an f64
287    pub fn to_float(&self) -> Result<f64, ValueError> {
288        self.check_tag(JsTag::Float64)?;
289        let val = unsafe { q::JS_Ext_GetFloat64(self.value) };
290        Ok(val)
291    }
292
293    /// Convert this value into a string
294    pub fn to_string(&self) -> Result<String, ValueError> {
295        self.check_tag(JsTag::String)
296            .or_else(|_| self.check_tag(JsTag::RopeString))?;
297        let ptr =
298            unsafe { q::JS_ToCStringLen2(self.context, std::ptr::null_mut(), self.value, false) };
299
300        if ptr.is_null() {
301            return Err(ValueError::Internal(
302                "Could not convert string: got a null pointer".into(),
303            ));
304        }
305
306        let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
307
308        let s = cstr
309            .to_str()
310            .map_err(ValueError::InvalidString)?
311            .to_string();
312
313        // Free the c string.
314        unsafe { q::JS_FreeCString(self.context, ptr) };
315
316        Ok(s)
317    }
318
319    pub fn to_array(&self) -> Result<OwnedJsArray, ValueError> {
320        OwnedJsArray::try_from_value(self.clone())
321    }
322
323    pub fn get_proxy_target(&self, recursive: bool) -> Result<OwnedJsValue, ValueError> {
324        if !self.is_proxy() {
325            return Err(ValueError::UnexpectedType);
326        }
327
328        let target = unsafe { q::JS_GetProxyTarget(self.context, self.value) };
329        let target = OwnedJsValue::new(self.context, target);
330
331        if recursive && target.is_proxy() {
332            target.get_proxy_target(true)
333        } else {
334            Ok(target)
335        }
336    }
337
338    /// Try convert this value into a object
339    pub fn try_into_object(self) -> Result<OwnedJsObject, ValueError> {
340        OwnedJsObject::try_from_value(self)
341    }
342
343    #[cfg(feature = "chrono")]
344    pub fn to_date(&self) -> Result<chrono::DateTime<chrono::Utc>, ValueError> {
345        use chrono::offset::TimeZone;
346
347        use crate::utils::js_date_constructor;
348
349        let date_constructor = js_date_constructor(self.context);
350        let is_date = unsafe { q::JS_IsInstanceOf(self.context, self.value, date_constructor) > 0 };
351
352        if is_date {
353            let getter = unsafe {
354                q::JS_GetPropertyStr(
355                    self.context,
356                    self.value,
357                    std::ffi::CStr::from_bytes_with_nul(b"getTime\0")
358                        .unwrap()
359                        .as_ptr(),
360                )
361            };
362            let tag = unsafe { q::JS_Ext_ValueGetTag(getter) };
363            assert_eq!(tag, q::JS_TAG_OBJECT);
364
365            let timestamp_raw =
366                unsafe { q::JS_Call(self.context, getter, self.value, 0, std::ptr::null_mut()) };
367
368            unsafe {
369                q::JS_FreeValue(self.context, getter);
370                q::JS_FreeValue(self.context, date_constructor);
371            };
372
373            let tag = unsafe { q::JS_Ext_ValueGetTag(timestamp_raw) };
374            if tag == q::JS_TAG_FLOAT64 {
375                let f = unsafe { q::JS_Ext_GetFloat64(timestamp_raw) } as i64;
376                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
377                Ok(datetime)
378            } else if tag == q::JS_TAG_INT {
379                let f = unsafe { q::JS_Ext_GetInt(timestamp_raw) } as i64;
380                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
381                Ok(datetime)
382            } else {
383                Err(ValueError::Internal(
384                    "Could not convert 'Date' instance to timestamp".into(),
385                ))
386            }
387        } else {
388            unsafe { q::JS_FreeValue(self.context, date_constructor) };
389            Err(ValueError::UnexpectedType)
390        }
391    }
392
393    #[cfg(feature = "bigint")]
394    pub fn to_bigint(&self) -> Result<crate::BigInt, ValueError> {
395        use crate::value::BigInt;
396        use crate::value::BigIntOrI64;
397
398        if self.is_int() {
399            let int = self.to_int()?;
400            return Ok(BigInt {
401                inner: BigIntOrI64::Int(int as i64),
402            });
403        }
404
405        // numbers between 2^53 -1 and i64::MAX is treated as float in quickjs
406        if self.is_float() {
407            let float = self.to_float()?;
408            return Ok(BigInt {
409                inner: BigIntOrI64::Int(float as i64),
410            });
411        }
412
413        if self.is_short_bigint() {
414            let int = unsafe { q::JS_Ext_GetShortBigInt(self.value) };
415            return Ok(BigInt {
416                inner: BigIntOrI64::Int(int as i64),
417            });
418        }
419
420        let ret = unsafe { q::JS_Ext_BigIntToString1(self.context, self.value, 16) };
421        let ret = OwnedJsValue::new(self.context, ret);
422
423        if ret.is_exception() {
424            let err = OwnedJsValue::new(self.context, unsafe { q::JS_GetException(self.context) });
425
426            return Err(ValueError::Internal(format!(
427                "Could not convert BigInt to string: {}",
428                err.js_to_string().unwrap()
429            )));
430        }
431
432        if !ret.is_string() {
433            return Err(ValueError::Internal(
434                "Could not convert BigInt: unexpected error".into(),
435            ));
436        }
437
438        let ret_str = ret.to_string().unwrap();
439
440        let bigint = num_bigint::BigInt::parse_bytes(ret_str.as_bytes(), 16).unwrap();
441
442        Ok(BigInt {
443            inner: BigIntOrI64::BigInt(bigint),
444        })
445        // }
446    }
447
448    /// Try convert this value into a function
449    pub fn try_into_function(self) -> Result<JsFunction, ValueError> {
450        JsFunction::try_from_value(self)
451    }
452
453    /// Try convert this value into a function
454    pub fn try_into_promise(self) -> Result<OwnedJsPromise, ValueError> {
455        OwnedJsPromise::try_from_value(self)
456    }
457
458    /// Try convert this value into a compiled function
459    pub fn try_into_compiled_function(self) -> Result<JsCompiledFunction, ValueError> {
460        JsCompiledFunction::try_from_value(self)
461    }
462
463    /// Try convert this value into a module
464    pub fn try_into_module(self) -> Result<JsModule, ValueError> {
465        JsModule::try_from_value(self)
466    }
467
468    /// Call the Javascript `.toString()` method on this value.
469    pub fn js_to_string(&self) -> Result<String, ExecutionError> {
470        let value = if self.is_string() {
471            self.to_string()?
472        } else {
473            let raw = unsafe { q::JS_ToString(self.context, self.value) };
474            let value = OwnedJsValue::new(self.context, raw);
475
476            if !value.is_string() {
477                return Err(ExecutionError::Internal(
478                    "Could not convert value to string".into(),
479                ));
480            }
481            value.to_string()?
482        };
483
484        Ok(value)
485    }
486
487    /// Call the Javascript `JSON.stringify()` method on this value.
488    pub fn to_json_string(&self, space: u8) -> Result<String, ExecutionError> {
489        let replacer = unsafe { q::JS_Ext_NewSpecialValue(q::JS_TAG_NULL, 0) };
490        let space = unsafe { q::JS_Ext_NewInt32(self.context, space as i32) };
491        let raw = unsafe { q::JS_JSONStringify(self.context, self.value, replacer, space) };
492
493        let value = OwnedJsValue::new(self.context, raw);
494
495        unsafe {
496            q::JS_FreeValue(self.context, replacer);
497            q::JS_FreeValue(self.context, space);
498        }
499
500        if !value.is_string() {
501            return Err(ExecutionError::Internal(
502                "Could not convert value to string".to_string(),
503            ));
504        }
505
506        let value = value.to_string()?;
507
508        Ok(value)
509    }
510
511    #[cfg(test)]
512    pub(crate) fn get_ref_count(&self) -> i32 {
513        unsafe { q::JS_Ext_GetRefCount(self.value) }
514    }
515}
516
517impl Drop for OwnedJsValue {
518    fn drop(&mut self) {
519        unsafe {
520            q::JS_FreeValue(self.context, self.value);
521        }
522    }
523}
524
525impl Clone for OwnedJsValue {
526    fn clone(&self) -> Self {
527        unsafe { q::JS_DupValue(self.context, self.value) };
528        Self {
529            context: self.context,
530            value: self.value,
531        }
532    }
533}
534
535impl std::fmt::Debug for OwnedJsValue {
536    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
537        write!(f, "{:?}(_)", self.tag())
538    }
539}
540
541impl TryFrom<OwnedJsValue> for bool {
542    type Error = ValueError;
543
544    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
545        value.to_bool()
546    }
547}
548
549impl TryFrom<OwnedJsValue> for i32 {
550    type Error = ValueError;
551
552    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
553        if value.is_int() {
554            return value.to_int();
555        } else if value.is_float() {
556            let f = value.to_float()?;
557            if f.fract() != 0.0 {
558                return Err(ValueError::UnexpectedType);
559            }
560            if f < (i32::MIN as f64) || f > (i32::MAX as f64) {
561                return Err(ValueError::OutOfRange);
562            }
563            return Ok(f as i32);
564        }
565        Err(ValueError::UnexpectedType)
566    }
567}
568
569impl TryFrom<OwnedJsValue> for f64 {
570    type Error = ValueError;
571
572    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
573        if value.is_float() {
574            return value.to_float();
575        } else if value.is_int() {
576            let i = value.to_int()?;
577            return Ok(i as f64);
578        }
579        Err(ValueError::UnexpectedType)
580    }
581}
582
583impl TryFrom<OwnedJsValue> for String {
584    type Error = ValueError;
585
586    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
587        value.to_string()
588    }
589}
590
591#[cfg(feature = "chrono")]
592impl TryFrom<OwnedJsValue> for DateTime<Utc> {
593    type Error = ValueError;
594
595    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
596        value.to_date()
597    }
598}
599
600#[cfg(feature = "bigint")]
601impl TryFrom<OwnedJsValue> for crate::BigInt {
602    type Error = ValueError;
603
604    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
605        value.to_bigint()
606    }
607}
608
609#[cfg(feature = "bigint")]
610impl TryFrom<OwnedJsValue> for i64 {
611    type Error = ValueError;
612
613    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
614        if value.is_int() {
615            value.to_int().map(|v| v as i64)
616        } else {
617            value
618                .to_bigint()
619                .and_then(|v| v.as_i64().ok_or(ValueError::BigIntOverflow))
620        }
621    }
622}
623
624#[cfg(feature = "bigint")]
625impl TryFrom<OwnedJsValue> for u64 {
626    type Error = ValueError;
627
628    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
629        use num_traits::ToPrimitive;
630        let bigint = value.to_bigint()?;
631        bigint
632            .into_bigint()
633            .to_u64()
634            .ok_or(ValueError::BigIntOverflow)
635    }
636}
637
638#[cfg(feature = "bigint")]
639impl TryFrom<OwnedJsValue> for i128 {
640    type Error = ValueError;
641
642    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
643        use num_traits::ToPrimitive;
644        let bigint = value.to_bigint()?;
645        bigint
646            .into_bigint()
647            .to_i128()
648            .ok_or(ValueError::BigIntOverflow)
649    }
650}
651
652#[cfg(feature = "bigint")]
653impl TryFrom<OwnedJsValue> for u128 {
654    type Error = ValueError;
655
656    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
657        use num_traits::ToPrimitive;
658        let bigint = value.to_bigint()?;
659        bigint
660            .into_bigint()
661            .to_u128()
662            .ok_or(ValueError::BigIntOverflow)
663    }
664}
665
666#[cfg(feature = "bigint")]
667impl TryFrom<OwnedJsValue> for num_bigint::BigInt {
668    type Error = ValueError;
669
670    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
671        value.to_bigint().map(|v| v.into_bigint())
672    }
673}
674
675impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Option<T> {
676    type Error = ValueError;
677
678    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
679        if value.is_null() {
680            return Ok(None);
681        }
682        Ok(Some(value.try_into()?))
683    }
684}
685
686impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Vec<T> {
687    type Error = ValueError;
688
689    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
690        let arr = value.to_array()?;
691        let mut ret: Vec<T> = vec![];
692        for i in 0..arr.length() {
693            let item = arr.get_index(i as u32).unwrap();
694            if let Some(item) = item {
695                let item = item.try_into()?;
696                ret.push(item);
697            }
698        }
699        Ok(ret)
700    }
701}
702
703impl<K: From<String> + PartialEq + Eq + Hash, V: TryFrom<OwnedJsValue, Error = ValueError>>
704    TryFrom<OwnedJsValue> for HashMap<K, V>
705{
706    type Error = ValueError;
707
708    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
709        let obj = value.try_into_object()?;
710        let mut ret: HashMap<K, V> = HashMap::new();
711        let mut iter = obj.properties_iter()?.step_by(2);
712        while let Some(Ok(key)) = iter.next() {
713            let key = key.to_string()?;
714            let item = obj.property(&key).unwrap();
715            if let Some(item) = item {
716                let item = item.try_into()?;
717                ret.insert(key.into(), item);
718            }
719        }
720        Ok(ret)
721    }
722}
723
724impl TryFrom<OwnedJsValue> for JsFunction {
725    type Error = ValueError;
726
727    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
728        JsFunction::try_from_value(value)
729    }
730}
731
732impl TryFrom<OwnedJsValue> for OwnedJsPromise {
733    type Error = ValueError;
734
735    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
736        OwnedJsPromise::try_from_value(value)
737    }
738}
739
740impl TryFrom<OwnedJsValue> for OwnedJsArray {
741    type Error = ValueError;
742
743    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
744        OwnedJsArray::try_from_value(value)
745    }
746}
747
748impl TryFrom<OwnedJsValue> for OwnedJsObject {
749    type Error = ValueError;
750
751    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
752        OwnedJsObject::try_from_value(value)
753    }
754}
755
756impl TryFrom<OwnedJsValue> for JsCompiledFunction {
757    type Error = ValueError;
758
759    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
760        JsCompiledFunction::try_from_value(value)
761    }
762}
763
764impl TryFrom<OwnedJsValue> for JsModule {
765    type Error = ValueError;
766
767    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
768        JsModule::try_from_value(value)
769    }
770}
771
772/// to avoid infinite recursion, we need to implement a ToOwnedJsValue trait for T,
773/// and then implement the `From<(*mut q::JSContext, T)>` trait for T and XXX<T> where T: ToOwnedJsValue
774///
775/// This trait should not be public, use the `From<(*mut q::JSContext, T)>` trait outside of this module.
776pub trait ToOwnedJsValue {
777    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue;
778}
779
780impl ToOwnedJsValue for bool {
781    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
782        let val = create_bool(context, self);
783        OwnedJsValue::new(context, val)
784    }
785}
786
787impl ToOwnedJsValue for i32 {
788    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
789        let val = create_int(context, self);
790        OwnedJsValue::new(context, val)
791    }
792}
793
794impl ToOwnedJsValue for i8 {
795    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
796        let val = create_int(context, self as i32);
797        OwnedJsValue::new(context, val)
798    }
799}
800
801impl ToOwnedJsValue for i16 {
802    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
803        let val = create_int(context, self as i32);
804        OwnedJsValue::new(context, val)
805    }
806}
807
808impl ToOwnedJsValue for u8 {
809    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
810        let val = create_int(context, self as i32);
811        OwnedJsValue::new(context, val)
812    }
813}
814
815impl ToOwnedJsValue for u16 {
816    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
817        let val = create_int(context, self as i32);
818        OwnedJsValue::new(context, val)
819    }
820}
821
822impl ToOwnedJsValue for f64 {
823    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
824        let val = create_float(context, self);
825        OwnedJsValue::new(context, val)
826    }
827}
828
829impl ToOwnedJsValue for u32 {
830    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
831        let val = create_float(context, self as f64);
832        OwnedJsValue::new(context, val)
833    }
834}
835
836impl ToOwnedJsValue for &str {
837    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
838        let val = create_string(context, self).unwrap();
839        OwnedJsValue::new(context, val)
840    }
841}
842
843impl ToOwnedJsValue for String {
844    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
845        let val = create_string(context, &self).unwrap();
846        OwnedJsValue::new(context, val)
847    }
848}
849
850#[cfg(feature = "chrono")]
851impl ToOwnedJsValue for DateTime<Utc> {
852    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
853        let val = create_date(context, self).unwrap();
854        OwnedJsValue::new(context, val)
855    }
856}
857
858#[cfg(feature = "bigint")]
859impl ToOwnedJsValue for crate::BigInt {
860    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
861        let val = create_bigint(context, self).unwrap();
862        OwnedJsValue::new(context, val)
863    }
864}
865
866#[cfg(feature = "bigint")]
867impl ToOwnedJsValue for num_bigint::BigInt {
868    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
869        let val = create_bigint(context, self.into()).unwrap();
870        OwnedJsValue::new(context, val)
871    }
872}
873
874#[cfg(feature = "bigint")]
875impl ToOwnedJsValue for i64 {
876    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
877        let val = create_bigint(context, self.into()).unwrap();
878        OwnedJsValue::new(context, val)
879    }
880}
881
882#[cfg(feature = "bigint")]
883impl ToOwnedJsValue for u64 {
884    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
885        let bigint: num_bigint::BigInt = self.into();
886        let val = create_bigint(context, bigint.into()).unwrap();
887        OwnedJsValue::new(context, val)
888    }
889}
890
891#[cfg(feature = "bigint")]
892impl ToOwnedJsValue for i128 {
893    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
894        let bigint: num_bigint::BigInt = self.into();
895        let val = create_bigint(context, bigint.into()).unwrap();
896        OwnedJsValue::new(context, val)
897    }
898}
899
900#[cfg(feature = "bigint")]
901impl ToOwnedJsValue for u128 {
902    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
903        let bigint: num_bigint::BigInt = self.into();
904        let val = create_bigint(context, bigint.into()).unwrap();
905        OwnedJsValue::new(context, val)
906    }
907}
908
909impl ToOwnedJsValue for JsFunction {
910    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
911        let val = create_function(context, self).unwrap();
912        OwnedJsValue::new(context, val)
913    }
914}
915
916impl ToOwnedJsValue for OwnedJsPromise {
917    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
918        let val = unsafe { self.into_value().extract() };
919        OwnedJsValue::new(context, val)
920    }
921}
922
923/// for some cases like HashMap<String, OwnedJsValue>
924impl ToOwnedJsValue for OwnedJsValue {
925    fn to_owned(self, _: *mut q::JSContext) -> OwnedJsValue {
926        self
927    }
928}
929
930impl<T> ToOwnedJsValue for Vec<T>
931where
932    T: ToOwnedJsValue,
933{
934    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
935        let arr = create_empty_array(context).unwrap();
936        self.into_iter().enumerate().for_each(|(idx, val)| {
937            let val: OwnedJsValue = (context, val).into();
938            add_array_element(context, arr, idx as u32, unsafe { val.extract() }).unwrap();
939        });
940
941        OwnedJsValue::new(context, arr)
942    }
943}
944
945impl<K, V> ToOwnedJsValue for HashMap<K, V>
946where
947    K: Into<String>,
948    V: ToOwnedJsValue,
949{
950    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
951        let obj = create_empty_object(context).unwrap();
952        self.into_iter().for_each(|(key, val)| {
953            let val: OwnedJsValue = (context, val).into();
954            add_object_property(context, obj, key.into().as_str(), unsafe { val.extract() })
955                .unwrap();
956        });
957
958        OwnedJsValue::new(context, obj)
959    }
960}
961
962impl<T> ToOwnedJsValue for Option<T>
963where
964    T: ToOwnedJsValue,
965{
966    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
967        if let Some(val) = self {
968            (context, val).into()
969        } else {
970            OwnedJsValue::new(context, create_null())
971        }
972    }
973}
974
975impl<T> ToOwnedJsValue for &T
976where
977    T: ToOwnedJsValue,
978{
979    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
980        (context, self).into()
981    }
982}
983
984impl<T> From<(*mut q::JSContext, T)> for OwnedJsValue
985where
986    T: ToOwnedJsValue,
987{
988    fn from((context, value): (*mut q::JSContext, T)) -> Self {
989        value.to_owned(context)
990    }
991}