1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::ffi::CStr;
use std::future::Future;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::ptr;

use crate::{check_status, sys, JsError, Result};

pub struct FuturePromise<Data, Resolver: FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>> {
  deferred: sys::napi_deferred,
  env: sys::napi_env,
  tsfn: sys::napi_threadsafe_function,
  async_resource_name: sys::napi_value,
  resolver: Resolver,
  _data: PhantomData<Data>,
}

unsafe impl<T, F: FnOnce(sys::napi_env, T) -> Result<sys::napi_value>> Send
  for FuturePromise<T, F>
{
}

impl<Data, Resolver: FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>>
  FuturePromise<Data, Resolver>
{
  pub fn new(env: sys::napi_env, deferred: sys::napi_deferred, resolver: Resolver) -> Result<Self> {
    let mut async_resource_name = ptr::null_mut();
    let s = unsafe { CStr::from_bytes_with_nul_unchecked(b"napi_resolve_promise_from_future\0") };
    check_status!(unsafe {
      sys::napi_create_string_utf8(env, s.as_ptr(), 32, &mut async_resource_name)
    })?;

    Ok(FuturePromise {
      deferred,
      resolver,
      env,
      tsfn: ptr::null_mut(),
      async_resource_name,
      _data: PhantomData,
    })
  }

  pub(crate) fn start(self) -> Result<TSFNValue> {
    let mut tsfn_value = ptr::null_mut();
    let async_resource_name = self.async_resource_name;
    let env = self.env;
    let self_ref = Box::leak(Box::from(self));
    check_status!(unsafe {
      sys::napi_create_threadsafe_function(
        env,
        ptr::null_mut(),
        ptr::null_mut(),
        async_resource_name,
        0,
        1,
        ptr::null_mut(),
        None,
        self_ref as *mut FuturePromise<Data, Resolver> as *mut c_void,
        Some(call_js_cb::<Data, Resolver>),
        &mut tsfn_value,
      )
    })?;
    self_ref.tsfn = tsfn_value;
    Ok(TSFNValue(tsfn_value))
  }
}

pub(crate) struct TSFNValue(sys::napi_threadsafe_function);

unsafe impl Send for TSFNValue {}

pub(crate) async fn resolve_from_future<Data: Send, Fut: Future<Output = Result<Data>>>(
  tsfn_value: TSFNValue,
  fut: Fut,
) {
  let val = fut.await;
  check_status!(unsafe {
    sys::napi_call_threadsafe_function(
      tsfn_value.0,
      Box::into_raw(Box::from(val)) as *mut c_void,
      sys::ThreadsafeFunctionCallMode::nonblocking,
    )
  })
  .expect("Failed to call thread safe function");
  check_status!(unsafe {
    sys::napi_release_threadsafe_function(tsfn_value.0, sys::ThreadsafeFunctionReleaseMode::release)
  })
  .expect("Failed to release thread safe function");
}

unsafe extern "C" fn call_js_cb<
  Data,
  Resolver: FnOnce(sys::napi_env, Data) -> Result<sys::napi_value>,
>(
  env: sys::napi_env,
  _js_callback: sys::napi_value,
  context: *mut c_void,
  data: *mut c_void,
) {
  let future_promise = Box::from_raw(context as *mut FuturePromise<Data, Resolver>);
  let value = Box::from_raw(data as *mut Result<Data>);
  let resolver = future_promise.resolver;
  let deferred = future_promise.deferred;
  let js_value_to_resolve = value.and_then(move |v| (resolver)(env, v));
  match js_value_to_resolve {
    Ok(v) => {
      let status = sys::napi_resolve_deferred(env, deferred, v);
      debug_assert!(status == sys::Status::napi_ok, "Resolve promise failed");
    }
    Err(e) => {
      let status = sys::napi_reject_deferred(
        env,
        deferred,
        if e.maybe_raw.is_null() {
          JsError::from(e).into_value(env)
        } else {
          let mut err = ptr::null_mut();
          let get_err_status = sys::napi_get_reference_value(env, e.maybe_raw, &mut err);
          debug_assert!(
            get_err_status == sys::Status::napi_ok,
            "Get Error from Reference failed"
          );
          let delete_reference_status = sys::napi_delete_reference(env, e.maybe_raw);
          debug_assert!(
            delete_reference_status == sys::Status::napi_ok,
            "Delete Error Reference failed"
          );
          err
        },
      );
      debug_assert!(status == sys::Status::napi_ok, "Reject promise failed");
    }
  };
}