use std::mem::MaybeUninit;
use crate::{
api::{self, napi_node_version},
prelude::*,
};
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct NapiEnv(pub(crate) napi_env);
impl AsRef<napi_env> for NapiEnv {
fn as_ref(&self) -> &napi_env {
&self.0
}
}
impl NapiEnv {
#[inline]
pub fn from_raw(env: napi_env) -> NapiEnv {
NapiEnv(env)
}
#[inline]
pub fn raw(&self) -> napi_env {
self.0
}
#[inline]
pub fn global(&self) -> NapiResult<JsGlobal> {
JsGlobal::new(*self)
}
#[inline]
pub fn node_version(&self) -> NapiResult<napi_node_version> {
let value = napi_call!(=napi_get_node_version, *self);
unsafe { Ok(std::ptr::read(value)) }
}
#[inline]
pub fn napi_version(&self) -> NapiResult<u32> {
Ok(napi_call!(=napi_get_version, *self))
}
#[inline]
pub fn null(&self) -> NapiResult<JsNull> {
JsNull::new(*self)
}
#[inline]
pub fn undefined(&self) -> NapiResult<JsUndefined> {
JsUndefined::new(*self)
}
#[inline]
pub fn int32(&self, value: i32) -> NapiResult<JsNumber> {
JsNumber::int32(*self, value)
}
#[inline]
pub fn uint32(&self, value: u32) -> NapiResult<JsNumber> {
JsNumber::uint32(*self, value)
}
#[inline]
pub fn int64(&self, value: i64) -> NapiResult<JsNumber> {
JsNumber::int64(*self, value)
}
#[inline]
pub fn double(&self, value: f64) -> NapiResult<JsNumber> {
JsNumber::double(*self, value)
}
#[inline]
pub fn string(&self, s: impl AsRef<str>) -> NapiResult<JsString> {
JsString::new(*self, s)
}
#[inline]
pub fn array(&self) -> NapiResult<JsArray> {
JsArray::empty(*self)
}
#[cfg(feature = "v6")]
#[inline]
pub fn bigint_i64(&self, value: i64) -> NapiResult<JsBigInt<i64>> {
JsBigInt::<i64>::new_i64(*self, value)
}
#[cfg(feature = "v6")]
#[inline]
pub fn bigint_u64(&self, value: u64) -> NapiResult<JsBigInt<u64>> {
JsBigInt::<u64>::new_u64(*self, value)
}
#[inline]
pub fn boolean(&self, boolean: bool) -> NapiResult<JsBoolean> {
JsBoolean::new(*self, boolean)
}
#[inline]
pub fn buffer<const N: usize>(&self) -> NapiResult<JsBuffer<N>> {
JsBuffer::<N>::create(*self)
}
#[inline]
pub fn buffer_copy<const N: usize>(&self, data: [u8; N]) -> NapiResult<JsBuffer<N>> {
JsBuffer::<N>::create_copy(*self, data)
}
#[inline]
pub fn arraybuffer(&self, buffer: impl AsRef<[u8]>) -> NapiResult<JsArrayBuffer> {
JsArrayBuffer::new(*self, buffer)
}
#[cfg(feature = "v5")]
pub fn date(&self, time: f64) -> NapiResult<JsDate> {
JsDate::new(*self, time)
}
#[inline]
pub fn symbol(&self) -> NapiResult<JsSymbol> {
JsSymbol::new(*self)
}
#[inline]
pub fn symbol_description(&self, desc: JsString) -> NapiResult<JsSymbol> {
JsSymbol::description(*self, desc)
}
#[inline]
pub fn object(&self) -> NapiResult<JsObject> {
JsObject::new(*self)
}
#[inline]
pub fn context(&self, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
NapiAsyncContext::new(*self, name)
}
#[inline]
pub fn external<T>(
&self,
value: T,
finalizer: impl FnOnce(NapiEnv, T) -> NapiResult<()> + 'static,
) -> NapiResult<JsExternal<T>> {
JsExternal::<T>::new(*self, value, finalizer)
}
#[inline]
pub fn func<T: FromJsArgs, R>(
&self,
func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
) -> NapiResult<Function<R>>
where
T: FromJsArgs,
R: NapiValueT,
{
Function::<R>::new(*self, Option::<String>::None, func)
}
#[inline]
pub fn func_named<T: FromJsArgs, R>(
&self,
name: impl AsRef<str>,
func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
) -> NapiResult<Function<R>>
where
T: FromJsArgs,
R: NapiValueT,
{
Function::<R>::new(*self, Some(name), func)
}
#[inline]
pub fn function_named(
&self,
name: impl AsRef<str>,
func: extern "C" fn(env: NapiEnv, info: napi_callback_info) -> napi_value,
) -> NapiResult<Function<JsValue>> {
let value = napi_call!(
=napi_create_function,
*self,
name.as_ref().as_ptr() as CharPointer,
name.as_ref().len(),
Some(func),
std::ptr::null_mut(),
);
Ok(Function::<JsValue>::from_value(JsValue::from_raw(
*self, value,
)))
}
#[inline]
pub fn function(
&self,
func: extern "C" fn(env: NapiEnv, info: napi_callback_info) -> napi_value,
) -> NapiResult<Function<JsValue>> {
let value = napi_call!(
=napi_create_function,
*self,
std::ptr::null(),
0,
Some(func),
std::ptr::null_mut(),
);
Ok(Function::<JsValue>::from_value(JsValue::from_raw(
*self, value,
)))
}
#[inline]
pub fn class<T, R>(
&self,
name: impl AsRef<str>,
func: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
properties: impl AsRef<[NapiPropertyDescriptor]>,
) -> NapiResult<JsClass>
where
T: FromJsArgs,
R: NapiValueT,
{
JsClass::new(*self, name, func, properties)
}
#[inline]
pub fn async_work<T>(
&self,
name: impl AsRef<str>,
state: T,
execute: impl FnMut(&mut T) + Send + 'static,
complete: impl FnMut(NapiEnv, NapiStatus, T) -> NapiResult<()> + 'static,
) -> NapiResult<NapiAsyncWork<T>> {
NapiAsyncWork::new(*self, name, state, execute, complete)
}
#[inline]
pub fn promise<T, L: NapiValueT + Copy + 'static, R: NapiValueT + Copy + 'static>(
&self,
mut work: impl FnMut(&mut T) + Send + 'static,
mut complete: impl FnMut(JsPromise<L, R>, NapiStatus, T) -> NapiResult<()> + 'static,
) -> NapiResult<JsPromise<L, R>>
where
T: Default,
{
JsPromise::<L, R>::spawn(*self, work, complete)
}
#[cfg(feature = "v4")]
#[inline]
pub fn tsfn<Data, R, const N: usize>(
&self,
name: impl AsRef<str>,
func: Function<R>,
finalizer: impl FnOnce(NapiEnv) -> NapiResult<()> + 'static,
callback: impl FnMut(Function<R>, Data) -> NapiResult<()> + 'static,
) -> NapiResult<NapiThreadsafeFunction<Data, N>>
where
R: NapiValueT,
{
NapiThreadsafeFunction::<Data, N>::new(*self, name, func, finalizer, callback)
}
#[inline]
pub fn define_properties(
&self,
object: impl NapiValueT,
properties: impl AsRef<[NapiPropertyDescriptor]>,
) -> NapiResult<()> {
napi_call!(
napi_define_properties,
*self,
object.raw(),
properties.as_ref().len(),
properties.as_ref().as_ptr() as *const _,
)
}
#[inline]
pub fn throw_error(&self, msg: impl AsRef<str>) -> NapiResult<()> {
use std::ffi::CString;
let msg = napi_s!(msg.as_ref())?;
napi_call!(napi_throw_error, *self, std::ptr::null(), msg.as_ptr())
}
#[inline]
pub fn throw_error_code(&self, msg: impl AsRef<str>, code: impl AsRef<str>) -> NapiResult<()> {
use std::ffi::CString;
let msg = napi_s!(msg.as_ref())?;
let code = napi_s!(code.as_ref())?;
napi_call!(napi_throw_error, *self, code.as_ptr(), msg.as_ptr())
}
#[inline]
pub fn throw_type_error(&self, msg: impl AsRef<str>) -> NapiResult<()> {
let msg = napi_s!(msg.as_ref()).map_err(|_| NapiStatus::StringExpected)?;
napi_call!(napi_throw_type_error, *self, std::ptr::null(), msg.as_ptr())
}
#[inline]
pub fn throw_type_error_code(
&self,
msg: impl AsRef<str>,
code: impl AsRef<str>,
) -> NapiResult<()> {
let msg = napi_s!(msg.as_ref()).map_err(|_| NapiStatus::StringExpected)?;
let code = napi_s!(code.as_ref())?;
napi_call!(napi_throw_type_error, *self, code.as_ptr(), msg.as_ptr())
}
#[inline]
pub fn throw_range_error(
&self,
msg: impl AsRef<str>,
code: Option<impl AsRef<str>>,
) -> NapiResult<()> {
use std::ffi::CString;
let msg = napi_s!(msg.as_ref())?;
napi_call!(
napi_throw_range_error,
*self,
std::ptr::null(),
msg.as_ptr()
)
}
#[inline]
pub fn throw_range_error_code(
&self,
msg: impl AsRef<str>,
code: impl AsRef<str>,
) -> NapiResult<()> {
use std::ffi::CString;
let msg = napi_s!(msg.as_ref())?;
let code = napi_s!(code.as_ref())?;
napi_call!(napi_throw_range_error, *self, code.as_ptr(), msg.as_ptr())
}
#[inline]
pub fn fatal_error(&self, msg: impl AsRef<str>) {
crate::fatal_error(msg, Option::<String>::None);
}
#[inline]
pub fn get_and_clear_last_exception(&self) -> NapiResult<Option<JsError>> {
let err = napi_call!(=napi_get_and_clear_last_exception, *self);
if err.is_null() {
Ok(None)
} else {
Ok(Some(JsError(JsValue(*self, err))))
}
}
#[inline]
pub fn get_last_error_info(&self) -> NapiResult<NapiExtendedErrorInfo> {
let info = napi_call!(=napi_get_last_error_info, *self);
unsafe { Ok(std::ptr::read(info)) }
}
#[inline]
pub fn is_exception_pending(&self) -> NapiResult<bool> {
Ok(napi_call!(=napi_is_exception_pending, *self))
}
pub fn error(&self, msg: impl AsRef<str>) -> NapiResult<JsError> {
JsError::error(*self, msg, Option::<String>::None)
}
#[inline]
#[cfg(feature = "v3")]
pub fn fatal_exception(&self, err: JsError) -> NapiResult<()> {
napi_call!(napi_fatal_exception, *self, err.raw())
}
#[inline]
pub fn handle_scope(&self) -> NapiResult<NapiHandleScope> {
NapiHandleScope::open(*self)
}
#[inline]
pub fn scope<T>(&self, task: impl Fn(NapiHandleScope) -> T) -> NapiResult<T> {
Ok(task(self.handle_scope()?))
}
#[inline]
pub fn escapable_handle_scope(&self) -> NapiResult<NapiEscapableHandleScope> {
NapiEscapableHandleScope::open(*self)
}
pub fn escapable_scope<T>(
&self,
task: impl Fn(NapiEscapableHandleScope) -> T,
) -> NapiResult<T> {
Ok(task(self.escapable_handle_scope()?))
}
#[cfg(feature = "v3")]
#[inline]
pub fn add_cleanup_hook<Hook>(&self, hook: Hook) -> NapiResult<CleanupHookHandler>
where
Hook: FnOnce() -> NapiResult<()>,
{
let hook: Box<Box<dyn FnOnce() -> NapiResult<()>>> = Box::new(Box::new(hook));
unsafe extern "C" fn cleanup_hook(data: *mut std::os::raw::c_void) {
unsafe {
let hook: Box<Box<dyn FnOnce() -> NapiResult<()>>> = Box::from_raw(data as _);
if let Err(e) = hook() {
log::error!("[{}] cleanup hook error.", e);
}
}
}
let args = Box::into_raw(hook) as _;
napi_call!(napi_add_env_cleanup_hook, *self, Some(cleanup_hook), args);
Ok(CleanupHookHandler {
env: *self,
hook: Some(cleanup_hook),
args,
})
}
#[cfg(feature = "v8")]
#[inline]
pub fn add_async_cleanup_hook<Hook>(
&self,
hook: Hook,
) -> NapiResult<Option<AsyncCleanupHookHandler>>
where
Hook: FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>,
{
let hook: Box<Box<dyn FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>>> =
Box::new(Box::new(hook));
unsafe extern "C" fn async_cleanup_hook(
handle: napi_async_cleanup_hook_handle,
data: *mut std::os::raw::c_void,
) {
unsafe {
let hook: Box<Box<dyn FnOnce(AsyncCleanupHookHandler) -> NapiResult<()>>> =
Box::from_raw(data as _);
if let Err(e) = hook(AsyncCleanupHookHandler(handle)) {
log::error!("[{}] cleanup hook error.", e);
}
}
}
let maybe_handler = napi_call!(
=napi_add_async_cleanup_hook,
*self,
Some(async_cleanup_hook),
Box::into_raw(hook) as _,
);
if maybe_handler.is_null() {
return Ok(None);
}
Ok(Some(AsyncCleanupHookHandler(maybe_handler)))
}
#[inline]
pub fn adjust_external_memory(&self, changes: i64) -> NapiResult<i64> {
Ok(napi_call!(=napi_adjust_external_memory, *self, changes))
}
#[inline]
pub fn run_script<R: NapiValueT>(&self, script: impl AsRef<str>) -> NapiResult<R> {
let result = napi_call!(
=napi_run_script,
*self,
JsString::new(*self, script)?.raw(),
);
Ok(R::from_raw(*self, result))
}
#[cfg(feature = "v2")]
#[inline]
pub fn get_uv_event_loop(&self) -> NapiResult<uv_loop_s> {
unsafe { Ok(*napi_call!(=napi_get_uv_event_loop, *self)) }
}
#[cfg(feature = "v6")]
#[allow(clippy::type_complexity)]
#[inline]
pub fn set_instance_data<T, F>(&self, data: T, finalizer: F) -> NapiResult<()>
where
F: FnOnce(NapiEnv, T) -> NapiResult<()>,
{
let data = Box::into_raw(Box::new(data)) as DataPointer;
unsafe extern "C" fn finalizer_trampoline<T>(
env: NapiEnv,
data: DataPointer,
finalizer: DataPointer,
) {
let finalizer: Box<Box<dyn FnOnce(NapiEnv, T) -> NapiResult<()>>> =
Box::from_raw(finalizer as _);
let data: Box<T> = Box::from_raw(data as _);
if let Err(err) = finalizer(env, *data) {
log::error!("NapiValueT::finalizer(): {}", err);
}
}
let finalizer: Box<Box<dyn FnOnce(NapiEnv, T) -> NapiResult<()>>> =
Box::new(Box::new(finalizer));
napi_call!(
napi_set_instance_data,
*self,
data,
Some(finalizer_trampoline::<T>),
Box::into_raw(finalizer) as _,
)
}
#[cfg(feature = "v6")]
#[inline]
pub fn get_instance_data<T>(&self) -> NapiResult<Option<&mut T>> {
let data = napi_call!(=napi_get_instance_data, *self) as *mut T;
if data.is_null() {
Ok(None)
} else {
unsafe { Ok(Some(&mut *data)) }
}
}
}
#[cfg(feature = "v3")]
pub struct CleanupHookHandler {
env: NapiEnv,
hook: Option<unsafe extern "C" fn(data: *mut std::os::raw::c_void)>,
args: *mut std::os::raw::c_void,
}
#[cfg(feature = "v3")]
impl CleanupHookHandler {
pub fn remove(self) -> NapiResult<()> {
napi_call!(napi_remove_env_cleanup_hook, self.env, self.hook, self.args)
}
}
#[cfg(feature = "v8")]
#[derive(Debug)]
pub struct AsyncCleanupHookHandler(napi_async_cleanup_hook_handle);
#[cfg(feature = "v8")]
impl AsyncCleanupHookHandler {
pub fn remove(self) -> NapiResult<()> {
napi_call!(napi_remove_async_cleanup_hook, self.0)
}
}