nodex-api 0.2.3

rust binding to node_api.h
Documentation
use crate::{api, prelude::*};
use std::{marker::PhantomData, mem::MaybeUninit};

#[derive(Clone, Debug)]
pub struct NapiAsyncContext {
    env: NapiEnv,
    context: napi_async_context,
}

impl NapiAsyncContext {
    pub(crate) fn from_raw(env: NapiEnv, context: napi_async_context) -> NapiAsyncContext {
        NapiAsyncContext { env, context }
    }

    pub fn env(&self) -> NapiEnv {
        self.env
    }

    pub fn raw(&self) -> napi_async_context {
        self.context
    }

    /// The async_resource object needs to be kept alive until napi_async_destroy to keep async_hooks related API acts correctly. In order to retain ABI compatibility with previous versions, napi_async_contexts are not maintaining the strong reference to the async_resource objects to avoid introducing causing memory leaks. However, if the async_resource is garbage collected by JavaScript engine before the napi_async_context was destroyed by napi_async_destroy, calling napi_async_context related APIs like napi_open_callback_scope and napi_make_callback can cause problems like loss of async context when using the AsyncLocalStorage API.
    /// In order to retain ABI compatibility with previous versions, passing NULL for async_resource does not result in an error. However, this is not recommended as this will result poor results with async_hooks init hooks and async_hooks.executionAsyncResource() as the resource is now required by the underlying async_hooks implementation in order to provide the linkage between async callbacks.
    pub fn new(env: NapiEnv, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
        let context = napi_call!(
            =napi_async_init,
            env,
            env.object()?.raw(),
            env.string(name)?.raw(),
        );

        Ok(NapiAsyncContext { env, context })
    }

    /// This API can be called even if there is a pending JavaScript exception.
    pub fn destroy(&mut self) -> NapiResult<()> {
        napi_call!(napi_async_destroy, self.env(), self.raw())
    }

    /// This method allows a JavaScript function object to be called from a native add-on.
    /// This API is similar to napi_call_function. However, it is used to call from native
    /// code back into JavaScript after returning from an async operation (when there is no
    /// other script on the stack). It is a fairly simple wrapper around node::MakeCallback.
    ///
    /// Note it is not necessary to use napi_make_callback from within a napi_async_complete_callback;
    /// in that situation the callback's async context has already been set up, so a direct call to
    /// napi_call_function is sufficient and appropriate. Use of the napi_make_callback function may
    /// be required when implementing custom async behavior that does not use napi_create_async_work.
    ///
    /// Any process.nextTicks or Promises scheduled on the microtask queue by JavaScript during
    /// he callback are ran before returning back to C/C++.
    pub fn make_callback<R, T>(&self, this: JsObject, func: Function<R>, args: T) -> NapiResult<R>
    where
        R: NapiValueT,
        T: ToJsArgs,
    {
        let env = self.env();
        let args = args
            .to_js_args(env)?
            .0
            .into_iter()
            .map(|value| value.raw())
            .collect::<Vec<_>>();

        let value = napi_call!(
            =napi_make_callback,
            self.env(),
            self.raw(),
            this.raw(),
            func.raw(),
            T::len(),
            args.as_ptr(),
        );

        Ok(R::from_raw(env, value))
    }

    #[cfg(feature = "v3")]
    /// There are cases (for example, resolving promises) where it is necessary to have the
    /// equivalent of the scope associated with a callback in place when making certain
    /// Node-API calls. If there is no other script on the stack the napi_open_callback_scope
    /// and napi_close_callback_scope functions can be used to open/close the required scope.
    pub fn scope(&self) -> NapiResult<NapiCallbackScope> {
        let env = self.env();
        let scope = napi_call!(
            =napi_open_callback_scope,
            env,
            env.object()?.raw(),
            self.raw(),
        );
        Ok(NapiCallbackScope::from_raw(env, scope))
    }
}

impl Drop for NapiAsyncContext {
    fn drop(&mut self) {
        self.destroy();
    }
}