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 #[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 #[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 #[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 args.resize(argc, ptr::null_mut());
280
281 Ok(JsCallback::new(JsEnv::new(self.0), this, args))
282 }
283
284 #[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 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 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 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 #[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 #[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 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 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 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 #[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_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 #[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 #[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 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 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 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 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
785pub trait ExtractFromJs<'a>: Sized {
787 fn label() -> &'static str {
788 std::any::type_name::<Self>()
789 }
790
791 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
817impl<'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 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 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
951pub 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 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
999pub struct JsObject {
1001 env: JsEnv,
1002 napi_value: napi_value,
1003}
1004
1005unsafe impl Send for JsObject {}
1006
1007impl JsObject {
1008 pub fn new(env: JsEnv, napi_value: napi_value) -> Self {
1010 Self { env, napi_value }
1011 }
1012
1013 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 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 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}