1use std::mem::MaybeUninit;
2
3use crate::{
4 api::{self, napi_node_version},
5 prelude::*,
6};
7
8#[repr(C)]
9#[derive(Clone, Copy, Debug)]
10pub struct NapiEnv(pub(crate) napi_env);
11
12impl AsRef<napi_env> for NapiEnv {
13 fn as_ref(&self) -> &napi_env {
14 &self.0
15 }
16}
17
18impl NapiEnv {
19 #[inline]
21 pub fn from_raw(env: napi_env) -> NapiEnv {
22 NapiEnv(env)
23 }
24
25 #[inline]
27 pub fn raw(&self) -> napi_env {
28 self.0
29 }
30
31 #[inline]
33 pub fn global(&self) -> NapiResult<JsGlobal> {
34 JsGlobal::new(*self)
35 }
36
37 #[inline]
40 pub fn node_version(&self) -> NapiResult<napi_node_version> {
41 let value = napi_call!(=napi_get_node_version, *self);
42 unsafe { Ok(std::ptr::read(value)) }
43 }
44
45 #[inline]
47 pub fn napi_version(&self) -> NapiResult<u32> {
48 Ok(napi_call!(=napi_get_version, *self))
49 }
50
51 #[inline]
53 pub fn null(&self) -> NapiResult<JsNull> {
54 JsNull::new(*self)
55 }
56
57 #[inline]
59 pub fn undefined(&self) -> NapiResult<JsUndefined> {
60 JsUndefined::new(*self)
61 }
62
63 #[inline]
66 pub fn int32(&self, value: i32) -> NapiResult<JsNumber> {
67 JsNumber::int32(*self, value)
68 }
69
70 #[inline]
73 pub fn uint32(&self, value: u32) -> NapiResult<JsNumber> {
74 JsNumber::uint32(*self, value)
75 }
76
77 #[inline]
80 pub fn int64(&self, value: i64) -> NapiResult<JsNumber> {
81 JsNumber::int64(*self, value)
82 }
83
84 #[inline]
87 pub fn double(&self, value: f64) -> NapiResult<JsNumber> {
88 JsNumber::double(*self, value)
89 }
90
91 #[inline]
94 pub fn string(&self, s: impl AsRef<str>) -> NapiResult<JsString> {
95 JsString::new(*self, s)
96 }
97
98 #[inline]
100 pub fn array(&self) -> NapiResult<JsArray> {
101 JsArray::empty(*self)
102 }
103
104 #[cfg(feature = "v6")]
105 #[inline]
107 pub fn bigint_i64(&self, value: i64) -> NapiResult<JsBigInt<i64>> {
108 JsBigInt::<i64>::new_i64(*self, value)
109 }
110
111 #[cfg(feature = "v6")]
112 #[inline]
114 pub fn bigint_u64(&self, value: u64) -> NapiResult<JsBigInt<u64>> {
115 JsBigInt::<u64>::new_u64(*self, value)
116 }
117
118 #[inline]
120 pub fn boolean(&self, boolean: bool) -> NapiResult<JsBoolean> {
121 JsBoolean::new(*self, boolean)
122 }
123
124 #[inline]
126 pub fn buffer<const N: usize>(&self) -> NapiResult<JsBuffer<N>> {
127 JsBuffer::<N>::create(*self)
128 }
129
130 #[inline]
132 pub fn buffer_copy<const N: usize>(&self, data: [u8; N]) -> NapiResult<JsBuffer<N>> {
133 JsBuffer::<N>::create_copy(*self, data)
134 }
135
136 #[inline]
138 pub fn arraybuffer(&self, buffer: impl AsRef<[u8]>) -> NapiResult<JsArrayBuffer> {
139 JsArrayBuffer::new(*self, buffer)
140 }
141
142 #[cfg(feature = "v5")]
143 pub fn date(&self, time: f64) -> NapiResult<JsDate> {
145 JsDate::new(*self, time)
146 }
147
148 #[inline]
151 pub fn symbol(&self) -> NapiResult<JsSymbol> {
152 JsSymbol::new(*self)
153 }
154
155 #[inline]
157 pub fn symbol_description(&self, desc: JsString) -> NapiResult<JsSymbol> {
158 JsSymbol::description(*self, desc)
159 }
160
161 #[inline]
164 pub fn object(&self) -> NapiResult<JsObject> {
165 JsObject::new(*self)
166 }
167
168 #[inline]
170 pub fn context(&self, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
171 NapiAsyncContext::new(*self, name)
172 }
173
174 #[inline]
176 pub fn external<T>(
177 &self,
178 value: T,
179 finalizer: impl FnOnce(NapiEnv, T) -> NapiResult<()> + 'static,
180 ) -> NapiResult<JsExternal<T>> {
181 JsExternal::<T>::new(*self, value, finalizer)
182 }
183
184 #[inline]
186 pub fn func<T: FromJsArgs, R>(
187 &self,
188 func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
189 ) -> NapiResult<Function<R>>
190 where
191 T: FromJsArgs,
192 R: NapiValueT,
193 {
194 Function::<R>::new(*self, Option::<String>::None, func)
195 }
196
197 #[inline]
199 pub fn func_named<T: FromJsArgs, R>(
200 &self,
201 name: impl AsRef<str>,
202 func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
203 ) -> NapiResult<Function<R>>
204 where
205 T: FromJsArgs,
206 R: NapiValueT,
207 {
208 Function::<R>::new(*self, Some(name), func)
209 }
210
211 #[inline]
213 pub fn function_named(
214 &self,
215 name: impl AsRef<str>,
216 func: extern "C" fn(env: NapiEnv, info: napi_callback_info) -> napi_value,
217 ) -> NapiResult<Function<JsValue>> {
218 let value = napi_call!(
219 =napi_create_function,
220 *self,
221 name.as_ref().as_ptr() as CharPointer,
222 name.as_ref().len(),
223 Some(func),
224 std::ptr::null_mut(),
225 );
226
227 Ok(Function::<JsValue>::from_value(JsValue::from_raw(
228 *self, value,
229 )))
230 }
231
232 #[inline]
234 pub fn function(
235 &self,
236 func: extern "C" fn(env: NapiEnv, info: napi_callback_info) -> napi_value,
237 ) -> NapiResult<Function<JsValue>> {
238 let value = napi_call!(
239 =napi_create_function,
240 *self,
241 std::ptr::null(),
242 0,
243 Some(func),
244 std::ptr::null_mut(),
245 );
246
247 Ok(Function::<JsValue>::from_value(JsValue::from_raw(
248 *self, value,
249 )))
250 }
251
252 #[inline]
254 pub fn class<T, R>(
255 &self,
256 name: impl AsRef<str>,
257 func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
258 properties: impl AsRef<[NapiPropertyDescriptor]>,
259 ) -> NapiResult<JsClass>
260 where
261 T: FromJsArgs,
262 R: NapiValueT,
263 {
264 JsClass::new(*self, name, func, properties)
265 }
266
267 #[inline]
269 pub fn async_work<T>(
270 &self,
271 name: impl AsRef<str>,
272 state: T,
273 execute: impl FnMut(&mut T) + Send + 'static,
274 complete: impl FnMut(NapiEnv, NapiStatus, T) -> NapiResult<()> + 'static,
275 ) -> NapiResult<NapiAsyncWork<T>> {
276 NapiAsyncWork::new(*self, name, state, execute, complete)
277 }
278
279 #[inline]
281 pub fn promise<T, L: NapiValueT + Copy + 'static, R: NapiValueT + Copy + 'static>(
282 &self,
283 mut work: impl FnMut(&mut T) + Send + 'static,
284 mut complete: impl FnMut(JsPromise<L, R>, NapiStatus, T) -> NapiResult<()> + 'static,
285 ) -> NapiResult<JsPromise<L, R>>
286 where
287 T: Default,
288 {
289 JsPromise::<L, R>::spawn(*self, work, complete)
290 }
291
292 #[cfg(feature = "v4")]
293 #[inline]
295 pub fn tsfn<Data, R, const N: usize>(
296 &self,
297 name: impl AsRef<str>,
298 func: Function<R>,
299 finalizer: impl FnOnce(NapiEnv) -> NapiResult<()> + 'static,
300 callback: impl FnMut(Function<R>, Data) -> NapiResult<()> + 'static,
301 ) -> NapiResult<NapiThreadsafeFunction<Data, N>>
302 where
303 R: NapiValueT,
304 {
305 NapiThreadsafeFunction::<Data, N>::new(*self, name, func, finalizer, callback)
306 }
307
308 #[inline]
314 pub fn define_properties(
315 &self,
316 object: impl NapiValueT,
317 properties: impl AsRef<[NapiPropertyDescriptor]>,
318 ) -> NapiResult<()> {
319 napi_call!(
320 napi_define_properties,
321 *self,
322 object.raw(),
323 properties.as_ref().len(),
324 properties.as_ref().as_ptr() as *const _,
325 )
326 }
327
328 #[inline]
330 pub fn throw<T: NapiValueT>(&self, to_throw: T) -> NapiResult<()> {
331 napi_call!(napi_throw, *self, to_throw.raw())
332 }
333
334 #[inline]
336 pub fn throw_error(&self, msg: impl AsRef<str>) -> NapiResult<()> {
337 use std::ffi::CString;
338 let msg = napi_s!(msg.as_ref())?;
339 napi_call!(napi_throw_error, *self, std::ptr::null(), msg.as_ptr())
340 }
341
342 #[inline]
344 pub fn throw_error_code(&self, msg: impl AsRef<str>, code: impl AsRef<str>) -> NapiResult<()> {
345 use std::ffi::CString;
346 let msg = napi_s!(msg.as_ref())?;
347 let code = napi_s!(code.as_ref())?;
348 napi_call!(napi_throw_error, *self, code.as_ptr(), msg.as_ptr())
349 }
350
351 #[inline]
353 pub fn throw_type_error(&self, msg: impl AsRef<str>) -> NapiResult<()> {
354 let msg = napi_s!(msg.as_ref()).map_err(|_| NapiStatus::StringExpected)?;
355 napi_call!(napi_throw_type_error, *self, std::ptr::null(), msg.as_ptr())
356 }
357
358 #[inline]
360 pub fn throw_type_error_code(
361 &self,
362 msg: impl AsRef<str>,
363 code: impl AsRef<str>,
364 ) -> NapiResult<()> {
365 let msg = napi_s!(msg.as_ref()).map_err(|_| NapiStatus::StringExpected)?;
366 let code = napi_s!(code.as_ref())?;
367 napi_call!(napi_throw_type_error, *self, code.as_ptr(), msg.as_ptr())
368 }
369
370 #[inline]
372 pub fn throw_range_error(
373 &self,
374 msg: impl AsRef<str>,
375 code: Option<impl AsRef<str>>,
376 ) -> NapiResult<()> {
377 use std::ffi::CString;
378 let msg = napi_s!(msg.as_ref())?;
379 napi_call!(
380 napi_throw_range_error,
381 *self,
382 std::ptr::null(),
383 msg.as_ptr()
384 )
385 }
386
387 #[inline]
389 pub fn throw_range_error_code(
390 &self,
391 msg: impl AsRef<str>,
392 code: impl AsRef<str>,
393 ) -> NapiResult<()> {
394 use std::ffi::CString;
395 let msg = napi_s!(msg.as_ref())?;
396 let code = napi_s!(code.as_ref())?;
397 napi_call!(napi_throw_range_error, *self, code.as_ptr(), msg.as_ptr())
398 }
399
400 #[inline]
401 pub fn fatal_error(&self, msg: impl AsRef<str>) {
402 crate::fatal_error(msg, Option::<String>::None);
403 }
404
405 #[inline]
408 pub fn get_and_clear_last_exception(&self) -> NapiResult<Option<JsError>> {
409 let err = napi_call!(=napi_get_and_clear_last_exception, *self);
410 if err.is_null() {
411 Ok(None)
412 } else {
413 Ok(Some(JsError(JsValue(*self, err))))
414 }
415 }
416
417 #[inline]
432 pub fn get_last_error_info(&self) -> NapiResult<NapiExtendedErrorInfo> {
433 let info = napi_call!(=napi_get_last_error_info, *self);
434 unsafe { Ok(std::ptr::read(info)) }
435 }
436
437 #[inline]
440 pub fn is_exception_pending(&self) -> NapiResult<bool> {
441 Ok(napi_call!(=napi_is_exception_pending, *self))
442 }
443
444 pub fn error(&self, msg: impl AsRef<str>) -> NapiResult<JsError> {
446 JsError::error(*self, msg, Option::<String>::None)
447 }
448
449 #[inline]
452 #[cfg(feature = "v3")]
453 pub fn fatal_exception(&self, err: JsError) -> NapiResult<()> {
454 napi_call!(napi_fatal_exception, *self, err.raw())
455 }
456
457 #[inline]
459 pub fn handle_scope(&self) -> NapiResult<NapiHandleScope> {
460 NapiHandleScope::open(*self)
461 }
462
463 #[inline]
465 pub fn scope<T>(&self, task: impl Fn(NapiHandleScope) -> T) -> NapiResult<T> {
466 Ok(task(self.handle_scope()?))
467 }
468
469 #[inline]
471 pub fn escapable_handle_scope(&self) -> NapiResult<NapiEscapableHandleScope> {
472 NapiEscapableHandleScope::open(*self)
473 }
474
475 pub fn escapable_scope<T>(
477 &self,
478 task: impl Fn(NapiEscapableHandleScope) -> T,
479 ) -> NapiResult<T> {
480 Ok(task(self.escapable_handle_scope()?))
481 }
482
483 #[cfg(feature = "v3")]
484 #[inline]
499 pub fn add_cleanup_hook<Hook>(&self, hook: Hook) -> NapiResult<CleanupHookHandler>
500 where
501 Hook: FnOnce() -> NapiResult<()>,
502 {
503 let hook: Box<Box<dyn FnOnce() -> NapiResult<()>>> = Box::new(Box::new(hook));
504
505 unsafe extern "C" fn cleanup_hook(data: *mut std::os::raw::c_void) {
506 unsafe {
507 let hook: Box<Box<dyn FnOnce() -> NapiResult<()>>> = Box::from_raw(data as _);
508 if let Err(e) = hook() {
509 log::error!("[{}] cleanup hook error.", e);
510 }
511 }
512 }
513
514 let args = Box::into_raw(hook) as _;
515
516 napi_call!(napi_add_env_cleanup_hook, *self, Some(cleanup_hook), args);
517
518 Ok(CleanupHookHandler {
519 env: *self,
520 hook: Some(cleanup_hook),
521 args,
522 })
523 }
524
525 #[cfg(feature = "v8")]
526 #[inline]
539 pub fn add_async_cleanup_hook<Hook>(
540 &self,
541 hook: Hook,
542 ) -> NapiResult<Option<AsyncCleanupHookHandler>>
543 where
544 Hook: FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>,
545 {
546 let hook: Box<Box<dyn FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>>> =
547 Box::new(Box::new(hook));
548
549 unsafe extern "C" fn async_cleanup_hook(
552 handle: napi_async_cleanup_hook_handle,
553 data: *mut std::os::raw::c_void,
554 ) {
555 unsafe {
556 let hook: Box<Box<dyn FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>>> =
557 Box::from_raw(data as _);
558 if let Err(e) = hook(AsyncCleanupHookHandler(handle)) {
559 log::error!("[{}] cleanup hook error.", e);
560 }
561 }
562 }
563
564 let maybe_handler = napi_call!(
565 =napi_add_async_cleanup_hook,
566 *self,
567 Some(async_cleanup_hook),
568 Box::into_raw(hook) as _,
569 );
570
571 if maybe_handler.is_null() {
572 return Ok(None);
573 }
574
575 Ok(Some(AsyncCleanupHookHandler(maybe_handler)))
576 }
577
578 #[inline]
583 pub fn adjust_external_memory(&self, changes: i64) -> NapiResult<i64> {
584 Ok(napi_call!(=napi_adjust_external_memory, *self, changes))
585 }
586
587 #[inline]
598 pub fn run_script<R: NapiValueT>(&self, script: impl AsRef<str>) -> NapiResult<R> {
599 let result = napi_call!(
600 =napi_run_script,
601 *self,
602 JsString::new(*self, script)?.raw(),
603 );
604 Ok(R::from_raw(*self, result))
605 }
606
607 #[cfg(feature = "v2")]
608 #[inline]
609 pub fn get_uv_event_loop(&self) -> NapiResult<uv_loop_s> {
610 unsafe { Ok(*napi_call!(=napi_get_uv_event_loop, *self)) }
611 }
612
613 #[cfg(feature = "v6")]
614 #[allow(clippy::type_complexity)]
615 #[inline]
616 pub fn set_instance_data<T, F>(&self, data: T, finalizer: F) -> NapiResult<()>
621 where
622 F: FnOnce(NapiEnv, T) -> NapiResult<()>,
623 {
624 let data = Box::into_raw(Box::new(data)) as DataPointer;
625
626 unsafe extern "C" fn finalizer_trampoline<T>(
630 env: NapiEnv,
631 data: DataPointer,
632 finalizer: DataPointer,
633 ) {
634 let finalizer: Box<Box<dyn FnOnce(NapiEnv, T) -> NapiResult<()>>> =
636 Box::from_raw(finalizer as _);
637
638 let data: Box<T> = Box::from_raw(data as _);
639
640 if let Err(err) = finalizer(env, *data) {
641 log::error!("NapiValueT::finalizer(): {}", err);
642 }
643 }
644
645 let finalizer: Box<Box<dyn FnOnce(NapiEnv, T) -> NapiResult<()>>> =
646 Box::new(Box::new(finalizer));
647
648 napi_call!(
649 napi_set_instance_data,
650 *self,
651 data,
652 Some(finalizer_trampoline::<T>),
653 Box::into_raw(finalizer) as _,
654 )
655 }
656
657 #[cfg(feature = "v6")]
658 #[inline]
662 pub fn get_instance_data<T>(&self) -> NapiResult<Option<&mut T>> {
663 let data = napi_call!(=napi_get_instance_data, *self) as *mut T;
664 if data.is_null() {
665 Ok(None)
666 } else {
667 unsafe { Ok(Some(&mut *data)) }
668 }
669 }
670}
671
672#[cfg(feature = "v3")]
673pub struct CleanupHookHandler {
674 env: NapiEnv,
675 hook: Option<unsafe extern "C" fn(data: *mut std::os::raw::c_void)>,
676 args: *mut std::os::raw::c_void,
677}
678
679#[cfg(feature = "v3")]
680impl CleanupHookHandler {
681 pub fn remove(self) -> NapiResult<()> {
682 napi_call!(napi_remove_env_cleanup_hook, self.env, self.hook, self.args)
683 }
684}
685
686#[cfg(feature = "v8")]
687#[derive(Debug)]
688pub struct AsyncCleanupHookHandler(napi_async_cleanup_hook_handle);
689
690#[cfg(feature = "v8")]
691impl AsyncCleanupHookHandler {
692 pub fn remove(self) -> NapiResult<()> {
693 napi_call!(napi_remove_async_cleanup_hook, self.0)
694 }
695}