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 function.
168    #[inline]
169    pub fn is_function(&self) -> bool {
170        unsafe { q::JS_IsFunction(self.context, self.value) }
171    }
172
173    /// Check if this value is a Javascript promise.
174    #[inline]
175    pub fn is_promise(&self) -> bool {
176        unsafe { q::JS_Ext_IsPromise(self.context, self.value) }
177    }
178
179    /// Check if this value is a Javascript module.
180    #[inline]
181    pub fn is_module(&self) -> bool {
182        self.tag().is_module()
183    }
184
185    /// Check if this value is a Javascript string.
186    #[inline]
187    pub fn is_string(&self) -> bool {
188        self.tag() == JsTag::String
189    }
190
191    /// Check if this value is a bytecode compiled function.
192    #[inline]
193    pub fn is_compiled_function(&self) -> bool {
194        self.tag() == JsTag::FunctionBytecode
195    }
196
197    #[inline]
198    fn check_tag(&self, expected: JsTag) -> Result<(), ValueError> {
199        if self.tag() == expected {
200            Ok(())
201        } else {
202            Err(ValueError::UnexpectedType)
203        }
204    }
205
206    /// Convert this value into a bool
207    pub fn to_bool(&self) -> Result<bool, ValueError> {
208        self.check_tag(JsTag::Bool)?;
209        let val = unsafe { q::JS_Ext_GetBool(self.value) };
210        Ok(val == 1)
211    }
212
213    /// Convert this value into an i32
214    pub fn to_int(&self) -> Result<i32, ValueError> {
215        self.check_tag(JsTag::Int)?;
216        let val = unsafe { q::JS_Ext_GetInt(self.value) };
217        Ok(val)
218    }
219
220    /// Convert this value into an f64
221    pub fn to_float(&self) -> Result<f64, ValueError> {
222        self.check_tag(JsTag::Float64)?;
223        let val = unsafe { q::JS_Ext_GetFloat64(self.value) };
224        Ok(val)
225    }
226
227    /// Convert this value into a string
228    pub fn to_string(&self) -> Result<String, ValueError> {
229        self.check_tag(JsTag::String)?;
230        let ptr =
231            unsafe { q::JS_ToCStringLen2(self.context, std::ptr::null_mut(), self.value, false) };
232
233        if ptr.is_null() {
234            return Err(ValueError::Internal(
235                "Could not convert string: got a null pointer".into(),
236            ));
237        }
238
239        let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
240
241        let s = cstr
242            .to_str()
243            .map_err(ValueError::InvalidString)?
244            .to_string();
245
246        // Free the c string.
247        unsafe { q::JS_FreeCString(self.context, ptr) };
248
249        Ok(s)
250    }
251
252    pub fn to_array(&self) -> Result<OwnedJsArray, ValueError> {
253        OwnedJsArray::try_from_value(self.clone())
254    }
255
256    /// Try convert this value into a object
257    pub fn try_into_object(self) -> Result<OwnedJsObject, ValueError> {
258        OwnedJsObject::try_from_value(self)
259    }
260
261    #[cfg(feature = "chrono")]
262    pub fn to_date(&self) -> Result<chrono::DateTime<chrono::Utc>, ValueError> {
263        use chrono::offset::TimeZone;
264
265        use crate::utils::js_date_constructor;
266
267        let date_constructor = js_date_constructor(self.context);
268        let is_date = unsafe { q::JS_IsInstanceOf(self.context, self.value, date_constructor) > 0 };
269
270        if is_date {
271            let getter = unsafe {
272                q::JS_GetPropertyStr(
273                    self.context,
274                    self.value,
275                    std::ffi::CStr::from_bytes_with_nul(b"getTime\0")
276                        .unwrap()
277                        .as_ptr(),
278                )
279            };
280            let tag = unsafe { q::JS_Ext_ValueGetTag(getter) };
281            assert_eq!(tag, q::JS_TAG_OBJECT);
282
283            let timestamp_raw =
284                unsafe { q::JS_Call(self.context, getter, self.value, 0, std::ptr::null_mut()) };
285
286            unsafe {
287                q::JS_FreeValue(self.context, getter);
288                q::JS_FreeValue(self.context, date_constructor);
289            };
290
291            let tag = unsafe { q::JS_Ext_ValueGetTag(timestamp_raw) };
292            if tag == q::JS_TAG_FLOAT64 {
293                let f = unsafe { q::JS_Ext_GetFloat64(timestamp_raw) } as i64;
294                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
295                Ok(datetime)
296            } else if tag == q::JS_TAG_INT {
297                let f = unsafe { q::JS_Ext_GetInt(timestamp_raw) } as i64;
298                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
299                Ok(datetime)
300            } else {
301                Err(ValueError::Internal(
302                    "Could not convert 'Date' instance to timestamp".into(),
303                ))
304            }
305        } else {
306            unsafe { q::JS_FreeValue(self.context, date_constructor) };
307            Err(ValueError::UnexpectedType)
308        }
309    }
310
311    #[cfg(feature = "bigint")]
312    pub fn to_bigint(&self) -> Result<crate::BigInt, ValueError> {
313        use crate::value::BigInt;
314        use crate::value::BigIntOrI64;
315
316        if self.is_int() {
317            let int = self.to_int()?;
318            return Ok(BigInt {
319                inner: BigIntOrI64::Int(int as i64),
320            });
321        }
322
323        // numbers between 2^53 -1 and i64::MAX is treated as float in quickjs
324        if self.is_float() {
325            let float = self.to_float()?;
326            return Ok(BigInt {
327                inner: BigIntOrI64::Int(float as i64),
328            });
329        }
330
331        if self.is_short_bigint() {
332            let int = unsafe { q::JS_Ext_GetShortBigInt(self.value) };
333            return Ok(BigInt {
334                inner: BigIntOrI64::Int(int as i64),
335            });
336        }
337
338        let ret = unsafe { q::JS_Ext_BigIntToString1(self.context, self.value, 16) };
339        let ret = OwnedJsValue::new(self.context, ret);
340
341        if ret.is_exception() {
342            let err = OwnedJsValue::new(self.context, unsafe { q::JS_GetException(self.context) });
343
344            return Err(ValueError::Internal(format!(
345                "Could not convert BigInt to string: {}",
346                err.js_to_string().unwrap()
347            )));
348        }
349
350        if !ret.is_string() {
351            return Err(ValueError::Internal(
352                "Could not convert BigInt: unexpected error".into(),
353            ));
354        }
355
356        let ret_str = ret.to_string().unwrap();
357
358        let bigint = num_bigint::BigInt::parse_bytes(ret_str.as_bytes(), 16).unwrap();
359
360        Ok(BigInt {
361            inner: BigIntOrI64::BigInt(bigint),
362        })
363        // }
364    }
365
366    /// Try convert this value into a function
367    pub fn try_into_function(self) -> Result<JsFunction, ValueError> {
368        JsFunction::try_from_value(self)
369    }
370
371    /// Try convert this value into a function
372    pub fn try_into_promise(self) -> Result<OwnedJsPromise, ValueError> {
373        OwnedJsPromise::try_from_value(self)
374    }
375
376    /// Try convert this value into a compiled function
377    pub fn try_into_compiled_function(self) -> Result<JsCompiledFunction, ValueError> {
378        JsCompiledFunction::try_from_value(self)
379    }
380
381    /// Try convert this value into a module
382    pub fn try_into_module(self) -> Result<JsModule, ValueError> {
383        JsModule::try_from_value(self)
384    }
385
386    /// Call the Javascript `.toString()` method on this value.
387    pub fn js_to_string(&self) -> Result<String, ExecutionError> {
388        let value = if self.is_string() {
389            self.to_string()?
390        } else {
391            let raw = unsafe { q::JS_ToString(self.context, self.value) };
392            let value = OwnedJsValue::new(self.context, raw);
393
394            if !value.is_string() {
395                return Err(ExecutionError::Internal(
396                    "Could not convert value to string".into(),
397                ));
398            }
399            value.to_string()?
400        };
401
402        Ok(value)
403    }
404
405    /// Call the Javascript `JSON.stringify()` method on this value.
406    pub fn to_json_string(&self, space: u8) -> Result<String, ExecutionError> {
407        let replacer = unsafe { q::JS_Ext_NewSpecialValue(q::JS_TAG_NULL, 0) };
408        let space = unsafe { q::JS_Ext_NewInt32(self.context, space as i32) };
409        let raw = unsafe { q::JS_JSONStringify(self.context, self.value, replacer, space) };
410
411        let value = OwnedJsValue::new(self.context, raw);
412
413        unsafe {
414            q::JS_FreeValue(self.context, replacer);
415            q::JS_FreeValue(self.context, space);
416        }
417
418        if !value.is_string() {
419            return Err(ExecutionError::Internal(
420                "Could not convert value to string".to_string(),
421            ));
422        }
423
424        let value = value.to_string()?;
425
426        Ok(value)
427    }
428
429    #[cfg(test)]
430    pub(crate) fn get_ref_count(&self) -> i32 {
431        unsafe { q::JS_Ext_GetRefCount(self.value) }
432    }
433}
434
435impl Drop for OwnedJsValue {
436    fn drop(&mut self) {
437        unsafe {
438            q::JS_FreeValue(self.context, self.value);
439        }
440    }
441}
442
443impl Clone for OwnedJsValue {
444    fn clone(&self) -> Self {
445        unsafe { q::JS_DupValue(self.context, self.value) };
446        Self {
447            context: self.context,
448            value: self.value,
449        }
450    }
451}
452
453impl std::fmt::Debug for OwnedJsValue {
454    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
455        write!(f, "{:?}(_)", self.tag())
456    }
457}
458
459impl TryFrom<OwnedJsValue> for bool {
460    type Error = ValueError;
461
462    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
463        value.to_bool()
464    }
465}
466
467impl TryFrom<OwnedJsValue> for i32 {
468    type Error = ValueError;
469
470    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
471        value.to_int()
472    }
473}
474
475impl TryFrom<OwnedJsValue> for f64 {
476    type Error = ValueError;
477
478    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
479        value.to_float()
480    }
481}
482
483impl TryFrom<OwnedJsValue> for String {
484    type Error = ValueError;
485
486    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
487        value.to_string()
488    }
489}
490
491#[cfg(feature = "chrono")]
492impl TryFrom<OwnedJsValue> for DateTime<Utc> {
493    type Error = ValueError;
494
495    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
496        value.to_date()
497    }
498}
499
500#[cfg(feature = "bigint")]
501impl TryFrom<OwnedJsValue> for crate::BigInt {
502    type Error = ValueError;
503
504    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
505        value.to_bigint()
506    }
507}
508
509#[cfg(feature = "bigint")]
510impl TryFrom<OwnedJsValue> for i64 {
511    type Error = ValueError;
512
513    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
514        if value.is_int() {
515            value.to_int().map(|v| v as i64)
516        } else {
517            value
518                .to_bigint()
519                .and_then(|v| v.as_i64().ok_or(ValueError::BigIntOverflow))
520        }
521    }
522}
523
524#[cfg(feature = "bigint")]
525impl TryFrom<OwnedJsValue> for u64 {
526    type Error = ValueError;
527
528    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
529        use num_traits::ToPrimitive;
530        let bigint = value.to_bigint()?;
531        bigint
532            .into_bigint()
533            .to_u64()
534            .ok_or(ValueError::BigIntOverflow)
535    }
536}
537
538#[cfg(feature = "bigint")]
539impl TryFrom<OwnedJsValue> for i128 {
540    type Error = ValueError;
541
542    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
543        use num_traits::ToPrimitive;
544        let bigint = value.to_bigint()?;
545        bigint
546            .into_bigint()
547            .to_i128()
548            .ok_or(ValueError::BigIntOverflow)
549    }
550}
551
552#[cfg(feature = "bigint")]
553impl TryFrom<OwnedJsValue> for u128 {
554    type Error = ValueError;
555
556    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
557        use num_traits::ToPrimitive;
558        let bigint = value.to_bigint()?;
559        bigint
560            .into_bigint()
561            .to_u128()
562            .ok_or(ValueError::BigIntOverflow)
563    }
564}
565
566#[cfg(feature = "bigint")]
567impl TryFrom<OwnedJsValue> for num_bigint::BigInt {
568    type Error = ValueError;
569
570    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
571        value.to_bigint().map(|v| v.into_bigint())
572    }
573}
574
575impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Option<T> {
576    type Error = ValueError;
577
578    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
579        if value.is_null() {
580            return Ok(None);
581        }
582        Ok(Some(value.try_into()?))
583    }
584}
585
586impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Vec<T> {
587    type Error = ValueError;
588
589    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
590        let arr = value.to_array()?;
591        let mut ret: Vec<T> = vec![];
592        for i in 0..arr.length() {
593            let item = arr.get_index(i as u32).unwrap();
594            if let Some(item) = item {
595                let item = item.try_into()?;
596                ret.push(item);
597            }
598        }
599        Ok(ret)
600    }
601}
602
603impl<K: From<String> + PartialEq + Eq + Hash, V: TryFrom<OwnedJsValue, Error = ValueError>>
604    TryFrom<OwnedJsValue> for HashMap<K, V>
605{
606    type Error = ValueError;
607
608    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
609        let obj = value.try_into_object()?;
610        let mut ret: HashMap<K, V> = HashMap::new();
611        let mut iter = obj.properties_iter()?.step_by(2);
612        while let Some(Ok(key)) = iter.next() {
613            let key = key.to_string()?;
614            let item = obj.property(&key).unwrap();
615            if let Some(item) = item {
616                let item = item.try_into()?;
617                ret.insert(key.into(), item);
618            }
619        }
620        Ok(ret)
621    }
622}
623
624impl TryFrom<OwnedJsValue> for JsFunction {
625    type Error = ValueError;
626
627    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
628        JsFunction::try_from_value(value)
629    }
630}
631
632impl TryFrom<OwnedJsValue> for OwnedJsPromise {
633    type Error = ValueError;
634
635    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
636        OwnedJsPromise::try_from_value(value)
637    }
638}
639
640impl TryFrom<OwnedJsValue> for OwnedJsArray {
641    type Error = ValueError;
642
643    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
644        OwnedJsArray::try_from_value(value)
645    }
646}
647
648impl TryFrom<OwnedJsValue> for OwnedJsObject {
649    type Error = ValueError;
650
651    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
652        OwnedJsObject::try_from_value(value)
653    }
654}
655
656impl TryFrom<OwnedJsValue> for JsCompiledFunction {
657    type Error = ValueError;
658
659    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
660        JsCompiledFunction::try_from_value(value)
661    }
662}
663
664impl TryFrom<OwnedJsValue> for JsModule {
665    type Error = ValueError;
666
667    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
668        JsModule::try_from_value(value)
669    }
670}
671
672/// to avoid infinite recursion, we need to implement a ToOwnedJsValue trait for T,
673/// and then implement the `From<(*mut q::JSContext, T)>` trait for T and XXX<T> where T: ToOwnedJsValue
674///
675/// This trait should not be public, use the `From<(*mut q::JSContext, T)>` trait outside of this module.
676pub trait ToOwnedJsValue {
677    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue;
678}
679
680impl ToOwnedJsValue for bool {
681    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
682        let val = create_bool(context, self);
683        OwnedJsValue::new(context, val)
684    }
685}
686
687impl ToOwnedJsValue for i32 {
688    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
689        let val = create_int(context, self);
690        OwnedJsValue::new(context, val)
691    }
692}
693
694impl ToOwnedJsValue for i8 {
695    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
696        let val = create_int(context, self as i32);
697        OwnedJsValue::new(context, val)
698    }
699}
700
701impl ToOwnedJsValue for i16 {
702    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
703        let val = create_int(context, self as i32);
704        OwnedJsValue::new(context, val)
705    }
706}
707
708impl ToOwnedJsValue for u8 {
709    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
710        let val = create_int(context, self as i32);
711        OwnedJsValue::new(context, val)
712    }
713}
714
715impl ToOwnedJsValue for u16 {
716    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
717        let val = create_int(context, self as i32);
718        OwnedJsValue::new(context, val)
719    }
720}
721
722impl ToOwnedJsValue for f64 {
723    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
724        let val = create_float(context, self);
725        OwnedJsValue::new(context, val)
726    }
727}
728
729impl ToOwnedJsValue for u32 {
730    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
731        let val = create_float(context, self as f64);
732        OwnedJsValue::new(context, val)
733    }
734}
735
736impl ToOwnedJsValue for &str {
737    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
738        let val = create_string(context, self).unwrap();
739        OwnedJsValue::new(context, val)
740    }
741}
742
743impl ToOwnedJsValue for String {
744    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
745        let val = create_string(context, &self).unwrap();
746        OwnedJsValue::new(context, val)
747    }
748}
749
750#[cfg(feature = "chrono")]
751impl ToOwnedJsValue for DateTime<Utc> {
752    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
753        let val = create_date(context, self).unwrap();
754        OwnedJsValue::new(context, val)
755    }
756}
757
758#[cfg(feature = "bigint")]
759impl ToOwnedJsValue for crate::BigInt {
760    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
761        let val = create_bigint(context, self).unwrap();
762        OwnedJsValue::new(context, val)
763    }
764}
765
766#[cfg(feature = "bigint")]
767impl ToOwnedJsValue for num_bigint::BigInt {
768    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
769        let val = create_bigint(context, self.into()).unwrap();
770        OwnedJsValue::new(context, val)
771    }
772}
773
774#[cfg(feature = "bigint")]
775impl ToOwnedJsValue for i64 {
776    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
777        let val = create_bigint(context, self.into()).unwrap();
778        OwnedJsValue::new(context, val)
779    }
780}
781
782#[cfg(feature = "bigint")]
783impl ToOwnedJsValue for u64 {
784    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
785        let bigint: num_bigint::BigInt = self.into();
786        let val = create_bigint(context, bigint.into()).unwrap();
787        OwnedJsValue::new(context, val)
788    }
789}
790
791#[cfg(feature = "bigint")]
792impl ToOwnedJsValue for i128 {
793    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
794        let bigint: num_bigint::BigInt = self.into();
795        let val = create_bigint(context, bigint.into()).unwrap();
796        OwnedJsValue::new(context, val)
797    }
798}
799
800#[cfg(feature = "bigint")]
801impl ToOwnedJsValue for u128 {
802    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
803        let bigint: num_bigint::BigInt = self.into();
804        let val = create_bigint(context, bigint.into()).unwrap();
805        OwnedJsValue::new(context, val)
806    }
807}
808
809impl ToOwnedJsValue for JsFunction {
810    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
811        let val = create_function(context, self).unwrap();
812        OwnedJsValue::new(context, val)
813    }
814}
815
816impl ToOwnedJsValue for OwnedJsPromise {
817    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
818        let val = unsafe { self.into_value().extract() };
819        OwnedJsValue::new(context, val)
820    }
821}
822
823/// for some cases like HashMap<String, OwnedJsValue>
824impl ToOwnedJsValue for OwnedJsValue {
825    fn to_owned(self, _: *mut q::JSContext) -> OwnedJsValue {
826        self
827    }
828}
829
830impl<T> ToOwnedJsValue for Vec<T>
831where
832    T: ToOwnedJsValue,
833{
834    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
835        let arr = create_empty_array(context).unwrap();
836        self.into_iter().enumerate().for_each(|(idx, val)| {
837            let val: OwnedJsValue = (context, val).into();
838            add_array_element(context, arr, idx as u32, unsafe { val.extract() }).unwrap();
839        });
840
841        OwnedJsValue::new(context, arr)
842    }
843}
844
845impl<K, V> ToOwnedJsValue for HashMap<K, V>
846where
847    K: Into<String>,
848    V: ToOwnedJsValue,
849{
850    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
851        let obj = create_empty_object(context).unwrap();
852        self.into_iter().for_each(|(key, val)| {
853            let val: OwnedJsValue = (context, val).into();
854            add_object_property(context, obj, key.into().as_str(), unsafe { val.extract() })
855                .unwrap();
856        });
857
858        OwnedJsValue::new(context, obj)
859    }
860}
861
862impl<T> ToOwnedJsValue for Option<T>
863where
864    T: ToOwnedJsValue,
865{
866    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
867        if let Some(val) = self {
868            (context, val).into()
869        } else {
870            OwnedJsValue::new(context, create_null())
871        }
872    }
873}
874
875impl<T> ToOwnedJsValue for &T
876where
877    T: ToOwnedJsValue,
878{
879    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
880        (context, self).into()
881    }
882}
883
884impl<T> From<(*mut q::JSContext, T)> for OwnedJsValue
885where
886    T: ToOwnedJsValue,
887{
888    fn from((context, value): (*mut q::JSContext, T)) -> Self {
889        value.to_owned(context)
890    }
891}