nj_core/
basic.rs

1use std::ptr;
2use std::ffi::CString;
3use std::collections::VecDeque;
4
5use tracing::instrument;
6use tracing::{error, debug, trace};
7
8use crate::sys::napi_env;
9use crate::sys::napi_value;
10use crate::sys::napi_callback_info;
11use crate::sys::napi_callback_raw;
12use crate::sys::napi_finalize_raw;
13use crate::sys::napi_valuetype;
14use crate::sys::napi_get_property;
15use crate::sys::napi_has_property;
16use crate::sys::napi_ref;
17use crate::sys::napi_deferred;
18use crate::sys::napi_threadsafe_function_call_js;
19
20use crate::napi_call_result;
21use crate::napi_call_assert;
22use crate::PropertiesBuilder;
23use crate::NjError;
24use crate::JSObjectWrapper;
25use crate::JSValue;
26use crate::TryIntoJs;
27
28fn napi_value_type_to_string(js_type: napi_valuetype) -> &'static str {
29    match js_type {
30        crate::sys::napi_valuetype_napi_bigint => "big_int",
31        crate::sys::napi_valuetype_napi_boolean => "bool",
32        crate::sys::napi_valuetype_napi_number => "number",
33        crate::sys::napi_valuetype_napi_string => "string",
34        crate::sys::napi_valuetype_napi_symbol => "symbol",
35        crate::sys::napi_valuetype_napi_function => "function",
36        crate::sys::napi_valuetype_napi_null => "null",
37        crate::sys::napi_valuetype_napi_external => "external",
38        crate::sys::napi_valuetype_napi_undefined => "undefined",
39        _ => "other",
40    }
41}
42
43#[derive(Clone, Copy, Debug)]
44pub struct JsEnv(napi_env);
45
46impl From<napi_env> for JsEnv {
47    fn from(env: napi_env) -> Self {
48        Self(env)
49    }
50}
51
52unsafe impl Send for JsEnv {}
53unsafe impl Sync for JsEnv {}
54
55impl JsEnv {
56    pub fn new(env: napi_env) -> Self {
57        Self(env)
58    }
59
60    pub fn inner(&self) -> napi_env {
61        self.0
62    }
63
64    pub fn create_string_utf8(&self, r_string: &str) -> Result<napi_value, NjError> {
65        trace!("create utf8 string: {}", r_string);
66        use nj_sys::napi_create_string_utf8;
67
68        let mut js_value = ptr::null_mut();
69        napi_call_result!(napi_create_string_utf8(
70            self.0,
71            r_string.as_ptr() as *const ::std::os::raw::c_char,
72            r_string.len(),
73            &mut js_value
74        ))?;
75        Ok(js_value)
76    }
77
78    pub fn create_string_utf8_from_bytes(&self, r_string: &[u8]) -> Result<napi_value, NjError> {
79        use nj_sys::napi_create_string_utf8;
80
81        let mut js_value = ptr::null_mut();
82        napi_call_result!(napi_create_string_utf8(
83            self.0,
84            r_string.as_ptr() as *const ::std::os::raw::c_char,
85            r_string.len(),
86            &mut js_value
87        ))?;
88        Ok(js_value)
89    }
90
91    pub fn create_double(&self, value: f64) -> Result<napi_value, NjError> {
92        let mut result = ptr::null_mut();
93        napi_call_result!(crate::sys::napi_create_double(self.0, value, &mut result))?;
94        Ok(result)
95    }
96
97    pub fn create_int64(&self, value: i64) -> Result<napi_value, NjError> {
98        let mut result = ptr::null_mut();
99        napi_call_result!(crate::sys::napi_create_int64(self.0, value, &mut result))?;
100        Ok(result)
101    }
102
103    pub fn create_int32(&self, value: i32) -> Result<napi_value, NjError> {
104        let mut result = ptr::null_mut();
105        napi_call_result!(crate::sys::napi_create_int32(self.0, value, &mut result))?;
106        Ok(result)
107    }
108
109    pub fn create_uint32(&self, value: u32) -> Result<napi_value, NjError> {
110        let mut result = ptr::null_mut();
111        napi_call_result!(crate::sys::napi_create_uint32(self.0, value, &mut result))?;
112        Ok(result)
113    }
114
115    pub fn create_bigint_uint64(&self, value: u64) -> Result<napi_value, NjError> {
116        let mut result = ptr::null_mut();
117        napi_call_result!(crate::sys::napi_create_bigint_uint64(
118            self.0,
119            value,
120            &mut result
121        ))?;
122        Ok(result)
123    }
124
125    pub fn create_object(&self) -> Result<napi_value, NjError> {
126        let mut result = ptr::null_mut();
127
128        napi_call_result!(crate::sys::napi_create_object(self.0, &mut result))?;
129        Ok(result)
130    }
131
132    pub fn create_boolean(&self, value: bool) -> Result<napi_value, NjError> {
133        let mut result = ptr::null_mut();
134        napi_call_result!(crate::sys::napi_get_boolean(self.0, value, &mut result,))?;
135        Ok(result)
136    }
137
138    pub fn create_array_with_len(&self, len: usize) -> Result<napi_value, NjError> {
139        let mut array = ptr::null_mut();
140
141        napi_call_result!(crate::sys::napi_create_array_with_length(
142            self.0, len, &mut array
143        ))?;
144        Ok(array)
145    }
146
147    #[allow(clippy::not_unsafe_ptr_arg_deref)]
148    pub fn set_element(
149        &self,
150        object: napi_value,
151        element: napi_value,
152        index: usize,
153    ) -> Result<(), NjError> {
154        napi_call_result!(crate::sys::napi_set_element(
155            self.0,
156            object,
157            index as u32,
158            element
159        ))?;
160        Ok(())
161    }
162
163    #[allow(clippy::not_unsafe_ptr_arg_deref)]
164    pub fn get_element(&self, array: napi_value, index: u32) -> Result<napi_value, NjError> {
165        let mut element = ptr::null_mut();
166
167        napi_call_result!(crate::sys::napi_get_element(
168            self.0,
169            array,
170            index,
171            &mut element
172        ))?;
173        Ok(element)
174    }
175
176    /// check if napi value is array
177    #[allow(clippy::not_unsafe_ptr_arg_deref)]
178    pub fn is_array(&self, array: napi_value) -> Result<bool, NjError> {
179        let mut result: bool = false;
180
181        napi_call_result!(crate::sys::napi_is_array(self.0, array, &mut result))?;
182
183        Ok(result)
184    }
185
186    /// check if napi value is array buffer
187    #[allow(clippy::not_unsafe_ptr_arg_deref)]
188    pub fn is_array_buffer(&self, array: napi_value) -> Result<bool, NjError> {
189        let mut result: bool = false;
190
191        napi_call_result!(crate::sys::napi_is_arraybuffer(self.0, array, &mut result))?;
192
193        Ok(result)
194    }
195
196    #[allow(clippy::not_unsafe_ptr_arg_deref)]
197    pub fn is_buffer(&self, n_value: napi_value) -> Result<bool, NjError> {
198        let mut result: bool = false;
199
200        napi_call_result!(crate::sys::napi_is_buffer(self.0, n_value, &mut result))?;
201
202        Ok(result)
203    }
204
205    #[allow(clippy::not_unsafe_ptr_arg_deref)]
206    pub fn is_date(&self, n_value: napi_value) -> Result<bool, NjError> {
207        let mut result: bool = false;
208
209        napi_call_result!(crate::sys::napi_is_date(self.0, n_value, &mut result))?;
210
211        Ok(result)
212    }
213
214    #[allow(clippy::not_unsafe_ptr_arg_deref)]
215    pub fn is_error(&self, n_value: napi_value) -> Result<bool, NjError> {
216        let mut result: bool = false;
217
218        napi_call_result!(crate::sys::napi_is_error(self.0, n_value, &mut result))?;
219
220        Ok(result)
221    }
222
223    pub fn get_global(&self) -> Result<napi_value, NjError> {
224        use nj_sys::napi_get_global;
225
226        let mut js_global = ptr::null_mut();
227        napi_call_result!(napi_get_global(self.0, &mut js_global))?;
228        Ok(js_global)
229    }
230
231    #[allow(clippy::not_unsafe_ptr_arg_deref)]
232    pub fn call_function(
233        &self,
234        recv: napi_value,
235        func: napi_value,
236        mut argv: Vec<napi_value>,
237    ) -> Result<napi_value, NjError> {
238        use nj_sys::napi_call_function;
239
240        let mut result = ptr::null_mut();
241
242        napi_call_result!(napi_call_function(
243            self.0,
244            recv,
245            func,
246            argv.len(),
247            argv.as_mut_ptr(),
248            &mut result
249        ))?;
250
251        Ok(result)
252    }
253
254    /// get callback information
255    #[allow(clippy::not_unsafe_ptr_arg_deref)]
256    #[instrument]
257    pub fn get_cb_info(
258        &self,
259        info: napi_callback_info,
260        max_count: usize,
261    ) -> Result<JsCallback, NjError> {
262        use nj_sys::napi_get_cb_info;
263
264        let mut this = ptr::null_mut();
265
266        let mut argc = max_count;
267        let mut args = vec![ptr::null_mut(); max_count];
268        napi_call_result!(napi_get_cb_info(
269            self.0,
270            info,
271            &mut argc,
272            args.as_mut_ptr(),
273            &mut this,
274            ptr::null_mut()
275        ))?;
276
277        trace!(argc, "actual argc");
278        // truncate arg to actual received count
279        args.resize(argc, ptr::null_mut());
280
281        Ok(JsCallback::new(JsEnv::new(self.0), this, args))
282    }
283
284    /// define classes
285    #[instrument(skip(properties))]
286    pub fn define_class(
287        &self,
288        name: &str,
289        constructor: napi_callback_raw,
290        properties: PropertiesBuilder,
291    ) -> Result<napi_value, NjError> {
292        debug!(?properties, "defining class",);
293        let mut js_constructor = ptr::null_mut();
294        let mut raw_properties = properties.as_raw_properties();
295        napi_call_result!(crate::sys::napi_define_class(
296            self.0,
297            name.as_ptr() as *const ::std::os::raw::c_char,
298            name.len(),
299            Some(constructor),
300            ptr::null_mut(),
301            raw_properties.len(),
302            raw_properties.as_mut_ptr(),
303            &mut js_constructor
304        ))?;
305
306        Ok(js_constructor)
307    }
308
309    #[allow(clippy::not_unsafe_ptr_arg_deref)]
310    pub fn create_reference(&self, cons: napi_value, count: u32) -> Result<napi_ref, NjError> {
311        let mut result = ptr::null_mut();
312        napi_call_result!(crate::sys::napi_create_reference(
313            self.0,
314            cons,
315            count,
316            &mut result
317        ))?;
318
319        Ok(result)
320    }
321
322    #[allow(clippy::not_unsafe_ptr_arg_deref)]
323    pub fn delete_reference(&self, ref_: napi_ref) -> Result<(), NjError> {
324        Ok(napi_call_result!(crate::sys::napi_delete_reference(
325            self.0, ref_
326        ))?)
327    }
328
329    #[allow(clippy::not_unsafe_ptr_arg_deref)]
330    #[instrument]
331    pub fn get_new_target(&self, info: napi_callback_info) -> Result<napi_value, NjError> {
332        let mut result = ptr::null_mut();
333        napi_call_result!(crate::sys::napi_get_new_target(self.0, info, &mut result))?;
334        debug!(?result, "got new target");
335        Ok(result)
336    }
337
338    #[allow(clippy::not_unsafe_ptr_arg_deref)]
339    #[instrument]
340    pub fn wrap(
341        &self,
342        js_object: napi_value,
343        rust_obj: *mut u8,
344        finalize: napi_finalize_raw,
345    ) -> Result<napi_ref, NjError> {
346        let mut result = ptr::null_mut();
347
348        debug!("napi wrap");
349        napi_call_result!(crate::sys::napi_wrap(
350            self.0,
351            js_object,
352            rust_obj as *mut core::ffi::c_void,
353            Some(finalize),
354            ptr::null_mut(),
355            &mut result
356        ))?;
357
358        Ok(result)
359    }
360
361    #[allow(clippy::not_unsafe_ptr_arg_deref)]
362    #[instrument]
363    pub fn unwrap<T>(&self, js_this: napi_value) -> Result<&'static T, NjError> {
364        let mut result: *mut ::std::os::raw::c_void = ptr::null_mut();
365        napi_call_result!(crate::sys::napi_unwrap(self.0, js_this, &mut result))?;
366
367        Ok(unsafe {
368            debug!(?result, "got back raw pointer");
369            if result.is_null() {
370                return Err(NjError::Other("unwrap got null pointer".to_string()));
371            }
372            let rust_ref: &T = &mut *(result as *mut T);
373            rust_ref
374        })
375    }
376
377    #[allow(clippy::not_unsafe_ptr_arg_deref)]
378    #[instrument]
379    pub fn unwrap_mut<T>(&self, js_this: napi_value) -> Result<&'static mut T, NjError> {
380        let mut result: *mut ::std::os::raw::c_void = ptr::null_mut();
381        debug!(env = ?self.0,"napi unwrap");
382        napi_call_result!(crate::sys::napi_unwrap(self.0, js_this, &mut result))?;
383        Ok(unsafe {
384            debug!(?result, "got back raw pointer");
385            if result.is_null() {
386                return Err(NjError::Other("unwrap mut null pointer".to_string()));
387            }
388            let ptr = result as *mut T;
389            let rust_ref: &mut T = &mut *(ptr);
390            rust_ref
391        })
392    }
393
394    #[allow(clippy::not_unsafe_ptr_arg_deref)]
395    pub fn new_instance(
396        &self,
397        constructor: napi_value,
398        mut args: Vec<napi_value>,
399    ) -> Result<napi_value, NjError> {
400        trace!(args = args.len(), "napi new instance");
401        let mut result = ptr::null_mut();
402        napi_call_result!(crate::sys::napi_new_instance(
403            self.0,
404            constructor,
405            args.len(),
406            args.as_mut_ptr(),
407            &mut result
408        ))?;
409
410        Ok(result)
411    }
412
413    #[allow(clippy::not_unsafe_ptr_arg_deref)]
414    pub fn get_reference_value(&self, obj_ref: napi_ref) -> Result<napi_value, NjError> {
415        let mut result = ptr::null_mut();
416        napi_call_result!(crate::sys::napi_get_reference_value(
417            self.0,
418            obj_ref,
419            &mut result
420        ))?;
421
422        Ok(result)
423    }
424
425    /// create promise and deferred
426    pub fn create_promise(&self) -> Result<(napi_value, napi_deferred), NjError> {
427        let mut deferred = ptr::null_mut();
428        let mut promise = ptr::null_mut();
429
430        napi_call_result!(crate::sys::napi_create_promise(
431            self.0,
432            &mut deferred,
433            &mut promise
434        ))?;
435
436        Ok((promise, deferred))
437    }
438
439    #[allow(clippy::not_unsafe_ptr_arg_deref)]
440    pub fn resolve_deferred(
441        &self,
442        deferred: napi_deferred,
443        resolution: napi_value,
444    ) -> Result<(), NjError> {
445        napi_call_result!(crate::sys::napi_resolve_deferred(
446            self.0, deferred, resolution
447        ))
448    }
449
450    #[allow(clippy::not_unsafe_ptr_arg_deref)]
451    pub fn reject_deferred(
452        &self,
453        deferred: napi_deferred,
454        rejection: napi_value,
455    ) -> Result<(), NjError> {
456        napi_call_result!(crate::sys::napi_reject_deferred(
457            self.0, deferred, rejection
458        ))
459    }
460
461    pub fn create_thread_safe_function(
462        &self,
463        name: &str,
464        js_func: Option<napi_value>,
465        call_js_cb: napi_threadsafe_function_call_js,
466    ) -> Result<crate::ThreadSafeFunction, NjError> {
467        use crate::sys::napi_create_threadsafe_function;
468
469        let work_name = self.create_string_utf8(name)?;
470
471        let mut tsfn = ptr::null_mut();
472
473        trace!("trying to create threadsafe fn: {}", name);
474
475        napi_call_result!(napi_create_threadsafe_function(
476            self.inner(),
477            js_func.unwrap_or(ptr::null_mut()),
478            ptr::null_mut(),
479            work_name,
480            0,
481            1,
482            ptr::null_mut(),
483            None,
484            ptr::null_mut(),
485            call_js_cb,
486            &mut tsfn
487        ))?;
488
489        trace!("created threadsafe fn: {}", name);
490
491        Ok(crate::ThreadSafeFunction::new(self.0, tsfn))
492    }
493
494    pub fn is_exception_pending(&self) -> bool {
495        let mut pending = false;
496        napi_call_assert!(crate::sys::napi_is_exception_pending(
497            self.inner(),
498            &mut pending
499        ));
500        pending
501    }
502
503    #[allow(clippy::not_unsafe_ptr_arg_deref)]
504    pub fn throw(&self, value: napi_value) {
505        debug!("throwing a native value");
506
507        // check if there is exception pending, if so log and not do anything
508        if self.is_exception_pending() {
509            error!(
510                "there is exception pending when trying to throw \
511                 a native value, ignoring for now",
512            );
513            return;
514        }
515
516        unsafe { crate::sys::napi_throw(self.inner(), value) };
517    }
518
519    pub fn throw_type_error(&self, message: &str) {
520        debug!(message, "type error");
521        // check if there is exception pending, if so log and not do anything
522        if self.is_exception_pending() {
523            error!(
524                "there is exception pending when trying to throw {}, ignoring for now",
525                message
526            );
527            return;
528        }
529
530        let c_error_msg = CString::new(message).expect("message should not contain null");
531        unsafe {
532            crate::sys::napi_throw_type_error(self.inner(), ptr::null_mut(), c_error_msg.as_ptr())
533        };
534    }
535
536    pub fn create_error(&self, message: &str) -> Result<napi_value, NjError> {
537        let mut result = ptr::null_mut();
538
539        let err_message = self.create_string_utf8(message)?;
540
541        napi_call_result!(crate::sys::napi_create_error(
542            self.0,
543            ptr::null_mut(),
544            err_message,
545            &mut result
546        ))?;
547
548        Ok(result)
549    }
550
551    /// get value type
552    #[allow(clippy::not_unsafe_ptr_arg_deref)]
553    pub fn value_type(&self, napi_value: napi_value) -> Result<napi_valuetype, NjError> {
554        use crate::sys::napi_typeof;
555
556        let mut valuetype: napi_valuetype = 0;
557
558        napi_call_result!(napi_typeof(self.inner(), napi_value, &mut valuetype))?;
559
560        Ok(valuetype)
561    }
562
563    /// is value undefined or null
564    #[allow(clippy::not_unsafe_ptr_arg_deref)]
565    pub fn is_undefined_or_null(&self, napi_value: napi_value) -> Result<bool, NjError> {
566        let valuetype = self.value_type(napi_value)?;
567        Ok(valuetype == crate::sys::napi_valuetype_napi_undefined
568            || valuetype == crate::sys::napi_valuetype_napi_null)
569    }
570
571    /// get string representation of value type
572    pub fn value_type_string(&self, napi_value: napi_value) -> Result<&'static str, NjError> {
573        Ok(napi_value_type_to_string(self.value_type(napi_value)?))
574    }
575
576    /// assert that napi value is certain type, otherwise raise exception
577    pub fn assert_type(
578        &self,
579        napi_value: napi_value,
580        should_be_type: napi_valuetype,
581    ) -> Result<(), NjError> {
582        let valuetype = self.value_type(napi_value)?;
583
584        if valuetype != should_be_type {
585            debug!(
586                "value type is: {}-{} but should be: {}-{}",
587                napi_value_type_to_string(valuetype),
588                valuetype,
589                napi_value_type_to_string(should_be_type),
590                should_be_type
591            );
592            Err(NjError::InvalidType(
593                napi_value_type_to_string(should_be_type).to_owned(),
594                napi_value_type_to_string(valuetype).to_owned(),
595            ))
596        } else {
597            Ok(())
598        }
599    }
600
601    /// convert napi value to rust value
602    pub fn convert_to_rust<'a, T>(&'a self, napi_value: napi_value) -> Result<T, NjError>
603    where
604        T: JSValue<'a>,
605    {
606        T::convert_to_rust(self, napi_value)
607    }
608
609    pub fn get_undefined(&self) -> Result<napi_value, NjError> {
610        let mut result = ptr::null_mut();
611
612        napi_call_result!(crate::sys::napi_get_undefined(self.0, &mut result))?;
613
614        Ok(result)
615    }
616    pub fn get_null(&self) -> Result<napi_value, NjError> {
617        let mut result = ptr::null_mut();
618        napi_call_result!(crate::sys::napi_get_null(self.0, &mut result))?;
619        Ok(result)
620    }
621
622    /// get buffer info
623    #[allow(clippy::not_unsafe_ptr_arg_deref)]
624    pub fn get_buffer_info(&self, napi_value: napi_value) -> Result<&[u8], NjError> {
625        use std::slice;
626        use crate::sys::napi_get_buffer_info;
627
628        let mut len = 0_usize;
629        let mut data = ptr::null_mut();
630
631        //  napi_status napi_get_buffer_info(napi_env env,
632        //      napi_value value,
633        //      void** data,
634        //      size_t* length)
635
636        napi_call_result!(napi_get_buffer_info(
637            self.inner(),
638            napi_value,
639            &mut data,
640            &mut len
641        ))?;
642
643        let array: &[u8] = unsafe { slice::from_raw_parts(data as *const u8, len) };
644
645        Ok(array)
646    }
647
648    /// Detach ArrayBuffer
649    #[allow(clippy::not_unsafe_ptr_arg_deref)]
650    pub fn detach_arraybuffer(&self, napi_value: napi_value) -> Result<(), NjError> {
651        napi_call_result!(crate::sys::napi_detach_arraybuffer(
652            self.inner(),
653            napi_value
654        ))?;
655        Ok(())
656    }
657
658    /// Is this ArrayBuffer Detached?
659    #[allow(clippy::not_unsafe_ptr_arg_deref)]
660    pub fn is_detached_arraybuffer(&self, napi_value: napi_value) -> Result<bool, NjError> {
661        let mut is_detached = false;
662        napi_call_result!(crate::sys::napi_is_detached_arraybuffer(
663            self.inner(),
664            napi_value,
665            &mut is_detached,
666        ))?;
667        Ok(is_detached)
668    }
669
670    #[allow(unused_unsafe)]
671    #[allow(clippy::missing_safety_doc)]
672    pub unsafe fn add_env_clean_up_hook(
673        &self,
674        init_func: Option<unsafe extern "C" fn(arg: *mut ::std::os::raw::c_void)>,
675        arg: *mut ::std::os::raw::c_void,
676    ) -> Result<(), NjError> {
677        use crate::sys::napi_add_env_cleanup_hook;
678
679        napi_call_result!(napi_add_env_cleanup_hook(self.inner(), init_func, arg))?;
680
681        Ok(())
682    }
683}
684
685#[derive(Clone, Debug)]
686pub struct JsCallback {
687    env: JsEnv,
688    this: napi_value,
689    args: VecDeque<napi_value>,
690}
691
692unsafe impl Send for JsCallback {}
693unsafe impl Sync for JsCallback {}
694
695impl JsCallback {
696    pub fn new(env: JsEnv, this: napi_value, args: Vec<napi_value>) -> Self {
697        Self {
698            env,
699            this,
700            args: args.into(),
701        }
702    }
703
704    pub fn env(&self) -> &JsEnv {
705        &self.env
706    }
707
708    pub fn args(&self, index: usize) -> napi_value {
709        self.args[index]
710    }
711
712    pub fn this(&self) -> napi_value {
713        self.this
714    }
715
716    pub fn this_owned(self) -> napi_value {
717        self.this
718    }
719
720    pub fn remove_napi(&mut self) -> Option<napi_value> {
721        self.args.remove(0)
722    }
723
724    /// consume next napi value and remove them from arg list
725    pub fn get_value<'a, T>(&'a mut self) -> Result<T, NjError>
726    where
727        T: ExtractFromJs<'a>,
728    {
729        trace!(args = self.args.len(), "arg len");
730
731        T::extract(self)
732    }
733
734    /// convert value to rust
735    pub fn get_value_at<'a, T>(&'a self, index: usize) -> Result<T, NjError>
736    where
737        T: ExtractArgFromJs<'a>,
738    {
739        trace!(index, "trying extract value at");
740        T::convert_arg_at(self, index)
741    }
742
743    /// create thread safe function
744    pub fn create_thread_safe_function(
745        &mut self,
746        name: &str,
747        call_js_cb: napi_threadsafe_function_call_js,
748    ) -> Result<crate::ThreadSafeFunction, NjError> {
749        if let Some(n_value) = self.remove_napi() {
750            self.env
751                .create_thread_safe_function(name, Some(n_value), call_js_cb)
752        } else {
753            Err(NjError::Other("expected js callback".to_owned()))
754        }
755    }
756
757    /// create thread safe function at
758    pub fn create_thread_safe_function_at(
759        &self,
760        name: &str,
761        index: usize,
762        call_js_cb: napi_threadsafe_function_call_js,
763    ) -> Result<crate::ThreadSafeFunction, NjError> {
764        if index < self.args.len() {
765            self.env
766                .create_thread_safe_function(name, Some(self.args[index]), call_js_cb)
767        } else {
768            Err(NjError::Other(format!("expected js callback at: {index}")))
769        }
770    }
771
772    #[instrument]
773    pub fn unwrap_mut<T>(&self) -> Result<&'static mut T, NjError> {
774        Ok(self
775            .env
776            .unwrap_mut::<JSObjectWrapper<T>>(self.this())?
777            .mut_inner())
778    }
779
780    pub fn unwrap<T>(&self) -> Result<&'static T, NjError> {
781        Ok(self.env.unwrap::<JSObjectWrapper<T>>(self.this())?.inner())
782    }
783}
784
785/// #[deprecated(since = "4.1.0","No longer used"]
786pub trait ExtractFromJs<'a>: Sized {
787    fn label() -> &'static str {
788        std::any::type_name::<Self>()
789    }
790
791    /// extract from js callback
792    fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError>;
793}
794
795impl<'a, T> ExtractFromJs<'a> for T
796where
797    T: JSValue<'a>,
798{
799    fn label() -> &'static str {
800        T::label()
801    }
802
803    #[instrument]
804    fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError> {
805        trace!("extract from ExtractFromJs");
806        if let Some(n_value) = js_cb.remove_napi() {
807            T::convert_to_rust(js_cb.env(), n_value)
808        } else {
809            Err(NjError::Other(format!(
810                "expected argument of type: {}",
811                Self::label()
812            )))
813        }
814    }
815}
816
817/// for optional argument
818impl<'a, T: Sized> ExtractFromJs<'a> for Option<T>
819where
820    T: JSValue<'a>,
821{
822    fn label() -> &'static str {
823        T::label()
824    }
825
826    fn extract(js_cb: &'a mut JsCallback) -> Result<Self, NjError> {
827        if let Some(n_value) = js_cb.remove_napi() {
828            Ok(Some(T::convert_to_rust(js_cb.env(), n_value)?))
829        } else {
830            Ok(None)
831        }
832    }
833}
834
835impl ExtractFromJs<'_> for JsEnv {
836    fn extract(js_cb: &mut JsCallback) -> Result<Self, NjError> {
837        Ok(*js_cb.env())
838    }
839}
840
841pub trait ExtractArgFromJs<'a>: Sized {
842    fn label() -> &'static str {
843        std::any::type_name::<Self>()
844    }
845
846    /// convert js callback argument at index
847    fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError>;
848}
849
850impl<'a, T> ExtractArgFromJs<'a> for T
851where
852    T: JSValue<'a>,
853{
854    fn label() -> &'static str {
855        T::label()
856    }
857
858    #[instrument]
859    fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError> {
860        trace!(
861            ty = std::any::type_name::<T>(),
862            "extract from ExtractArgFromJs"
863        );
864        if index < js_cb.args.len() {
865            T::convert_to_rust(js_cb.env(), js_cb.args[index])
866        } else {
867            Err(NjError::Other(format!(
868                "trying to get arg at: {} but only {} args passed",
869                index,
870                js_cb.args.len()
871            )))
872        }
873    }
874}
875
876impl<'a, T: Sized> ExtractArgFromJs<'a> for Option<T>
877where
878    T: JSValue<'a>,
879{
880    fn label() -> &'static str {
881        T::label()
882    }
883
884    fn convert_arg_at(js_cb: &'a JsCallback, index: usize) -> Result<Self, NjError> {
885        if index < js_cb.args.len() {
886            if js_cb.env().is_undefined_or_null(js_cb.args[index])? {
887                Ok(None)
888            } else {
889                Ok(Some(T::convert_to_rust(js_cb.env(), js_cb.args[index])?))
890            }
891        } else {
892            Ok(None)
893        }
894    }
895}
896
897impl ExtractArgFromJs<'_> for JsEnv {
898    fn convert_arg_at(js_cb: &JsCallback, _index: usize) -> Result<Self, NjError> {
899        Ok(*js_cb.env())
900    }
901}
902
903#[derive(Debug)]
904pub struct JsExports {
905    inner: napi_value,
906    env: JsEnv,
907}
908
909impl JsExports {
910    pub fn new(env: napi_env, exports: napi_value) -> Self {
911        Self {
912            inner: exports,
913            env: JsEnv::new(env),
914        }
915    }
916
917    pub fn env(&self) -> &JsEnv {
918        &self.env
919    }
920
921    pub fn prop_builder(&self) -> PropertiesBuilder {
922        PropertiesBuilder::new()
923    }
924
925    pub fn define_property(&self, properties: PropertiesBuilder) -> Result<(), NjError> {
926        // it is important not to release properties until this call is executed
927        // since it is source of name string
928        let mut raw_properties = properties.as_raw_properties();
929
930        napi_call_result!(crate::sys::napi_define_properties(
931            self.env.inner(),
932            self.inner,
933            raw_properties.len(),
934            raw_properties.as_mut_ptr()
935        ))
936    }
937
938    #[allow(clippy::not_unsafe_ptr_arg_deref)]
939    pub fn set_name_property(&self, name: &str, js_class: napi_value) -> Result<(), NjError> {
940        let c_name = CString::new(name).expect("should work");
941
942        napi_call_result!(crate::sys::napi_set_named_property(
943            self.env.inner(),
944            self.inner,
945            c_name.as_ptr(),
946            js_class
947        ))
948    }
949}
950
951/// Js callback function
952pub struct JsCallbackFunction {
953    ctx: napi_value,
954    js_func: napi_value,
955    env: JsEnv,
956}
957
958unsafe impl Send for JsCallbackFunction {}
959unsafe impl Sync for JsCallbackFunction {}
960
961impl JSValue<'_> for JsCallbackFunction {
962    fn label() -> &'static str {
963        "callback"
964    }
965
966    fn convert_to_rust(env: &JsEnv, js_value: napi_value) -> Result<Self, NjError> {
967        env.assert_type(js_value, crate::sys::napi_valuetype_napi_function)?;
968
969        let ctx = env.get_global()?;
970        Ok(Self {
971            ctx,
972            js_func: js_value,
973            env: *env,
974        })
975    }
976}
977
978impl JsCallbackFunction {
979    /// invoke
980    pub fn call<T>(&self, rust_argv: Vec<T>) -> Result<napi_value, NjError>
981    where
982        T: TryIntoJs,
983    {
984        trace!("invoking normal js callback");
985
986        let env = &self.env;
987        let mut argv: Vec<napi_value> = vec![];
988        for rust_arg in rust_argv {
989            match rust_arg.try_to_js(env) {
990                Ok(js_value) => argv.push(js_value),
991                Err(err) => return Err(err),
992            }
993        }
994
995        self.env.call_function(self.ctx, self.js_func, argv)
996    }
997}
998
999/// represent arbitrary js object
1000pub struct JsObject {
1001    env: JsEnv,
1002    napi_value: napi_value,
1003}
1004
1005unsafe impl Send for JsObject {}
1006
1007impl JsObject {
1008    /// create js object from js object
1009    pub fn new(env: JsEnv, napi_value: napi_value) -> Self {
1010        Self { env, napi_value }
1011    }
1012
1013    /// create new js object from env
1014    pub fn create(env: &JsEnv) -> Result<Self, NjError> {
1015        Ok(Self::new(*env, env.create_object()?))
1016    }
1017
1018    pub fn env(&self) -> &JsEnv {
1019        &self.env
1020    }
1021
1022    pub fn napi_value(&self) -> napi_value {
1023        self.napi_value
1024    }
1025
1026    /// get property
1027    pub fn get_property(&self, key: &str) -> Result<Option<Self>, NjError> {
1028        let property_key = self.env.create_string_utf8(key)?;
1029
1030        let mut exist: bool = false;
1031        napi_call_result!(napi_has_property(
1032            self.env.inner(),
1033            self.napi_value,
1034            property_key,
1035            &mut exist,
1036        ))?;
1037
1038        if !exist {
1039            return Ok(None);
1040        }
1041
1042        let mut property_value = ptr::null_mut();
1043
1044        napi_call_result!(napi_get_property(
1045            self.env.inner(),
1046            self.napi_value,
1047            property_key,
1048            &mut property_value,
1049        ))?;
1050
1051        Ok(Some(Self {
1052            env: self.env,
1053            napi_value: property_value,
1054        }))
1055    }
1056
1057    #[allow(clippy::not_unsafe_ptr_arg_deref)]
1058    pub fn set_property(&mut self, key: &str, property_value: napi_value) -> Result<(), NjError> {
1059        use crate::sys::napi_set_property;
1060
1061        let property_key = self.env.create_string_utf8(key)?;
1062
1063        napi_call_result!(napi_set_property(
1064            self.env.inner(),
1065            self.napi_value,
1066            property_key,
1067            property_value,
1068        ))?;
1069
1070        Ok(())
1071    }
1072
1073    /// convert to equivalent rust object
1074    pub fn as_value<'a, T>(&'a self) -> Result<T, NjError>
1075    where
1076        T: JSValue<'a>,
1077    {
1078        self.env.convert_to_rust(self.napi_value)
1079    }
1080}
1081
1082impl JSValue<'_> for JsObject {
1083    fn convert_to_rust(env: &JsEnv, js_value: napi_value) -> Result<Self, NjError> {
1084        env.assert_type(js_value, crate::sys::napi_valuetype_napi_object)?;
1085        Ok(Self::new(*env, js_value))
1086    }
1087}
1088
1089impl TryIntoJs for JsObject {
1090    fn try_to_js(self, _js_env: &JsEnv) -> Result<napi_value, NjError> {
1091        Ok(self.napi_value)
1092    }
1093}