1use std::ffi::c_void;
2use std::marker::PhantomData;
3use std::ptr;
4
5#[cfg(all(feature = "napi4", feature = "tokio_rt"))]
6use crate::bindgen_runtime::Promise;
7use crate::{
8 bindgen_prelude::{
9 FromNapiValue, JsObjectValue, Result, ToNapiValue, TypeName, ValidateNapiValue,
10 },
11 check_status, sys, Env, Error, JsValue, Value, ValueType,
12};
13
14#[derive(Clone, Copy)]
15pub struct PromiseRaw<'env, T> {
16 pub(crate) inner: sys::napi_value,
17 env: sys::napi_env,
18 _phantom: &'env PhantomData<T>,
19}
20
21impl<'env, T> JsValue<'env> for PromiseRaw<'env, T> {
22 fn value(&self) -> Value {
23 Value {
24 env: self.env,
25 value: self.inner,
26 value_type: ValueType::Object,
27 }
28 }
29}
30
31impl<'env, T> JsObjectValue<'env> for PromiseRaw<'env, T> {}
32
33impl<T> TypeName for PromiseRaw<'_, T> {
34 fn type_name() -> &'static str {
35 "Promise"
36 }
37
38 fn value_type() -> crate::ValueType {
39 crate::ValueType::Object
40 }
41}
42
43impl<T> ValidateNapiValue for PromiseRaw<'_, T> {
44 unsafe fn validate(
45 env: napi_sys::napi_env,
46 napi_val: napi_sys::napi_value,
47 ) -> Result<napi_sys::napi_value> {
48 validate_promise(env, napi_val)
49 }
50}
51
52impl<T> FromNapiValue for PromiseRaw<'_, T> {
53 unsafe fn from_napi_value(env: napi_sys::napi_env, value: napi_sys::napi_value) -> Result<Self> {
54 Ok(PromiseRaw::new(env, value))
55 }
56}
57
58impl<T> PromiseRaw<'_, T> {
59 pub(crate) fn new(env: sys::napi_env, inner: sys::napi_value) -> Self {
60 Self {
61 inner,
62 env,
63 _phantom: &PhantomData,
64 }
65 }
66}
67
68impl<'env, T: FromNapiValue> PromiseRaw<'env, T> {
69 pub fn then<'then, Callback, U>(&self, cb: Callback) -> Result<PromiseRaw<'env, U>>
71 where
72 U: ToNapiValue,
73 Callback: 'then + FnOnce(CallbackContext<T>) -> Result<U>,
74 {
75 let mut then_fn = ptr::null_mut();
76 const THEN: &[u8; 5] = b"then\0";
77 check_status!(unsafe {
78 sys::napi_get_named_property(self.env, self.inner, THEN.as_ptr().cast(), &mut then_fn)
79 })?;
80 let mut then_callback = ptr::null_mut();
81 let executed = Box::into_raw(Box::new(false));
82 let rust_cb = Box::into_raw(Box::new((cb, executed)));
83 check_status!(
84 unsafe {
85 sys::napi_create_function(
86 self.env,
87 THEN.as_ptr().cast(),
88 4,
89 Some(raw_promise_then_callback::<T, U, Callback>),
90 rust_cb.cast(),
91 &mut then_callback,
92 )
93 },
94 "Create then function for PromiseRaw failed"
95 )?;
96 let mut new_promise = ptr::null_mut();
97 check_status!(
98 unsafe {
99 sys::napi_call_function(
100 self.env,
101 self.inner,
102 then_fn,
103 1,
104 [then_callback].as_ptr(),
105 &mut new_promise,
106 )
107 },
108 "Call the PromiseRaw::then failed"
109 )?;
110
111 check_status!(
114 unsafe {
115 sys::napi_wrap(
116 self.env,
117 new_promise,
118 executed.cast(),
119 Some(promise_callback_finalizer::<T, U, Callback>),
120 rust_cb.cast(),
121 ptr::null_mut(),
122 )
123 },
124 "Wrap finalizer for PromiseRaw failed"
125 )?;
126
127 Ok(PromiseRaw::<U> {
128 env: self.env,
129 inner: new_promise,
130 _phantom: &PhantomData,
131 })
132 }
133
134 pub fn catch<'catch, E, U, Callback>(&self, cb: Callback) -> Result<PromiseRaw<'env, U>>
136 where
137 E: FromNapiValue,
138 U: ToNapiValue,
139 Callback: 'catch + FnOnce(CallbackContext<E>) -> Result<U>,
140 {
141 let mut catch_fn = ptr::null_mut();
142 const CATCH: &[u8; 6] = b"catch\0";
143 check_status!(unsafe {
144 sys::napi_get_named_property(self.env, self.inner, CATCH.as_ptr().cast(), &mut catch_fn)
145 })?;
146 let mut catch_callback = ptr::null_mut();
147 let executed = Box::into_raw(Box::new(false));
148 let rust_cb = Box::into_raw(Box::new((cb, executed)));
149 check_status!(
150 unsafe {
151 sys::napi_create_function(
152 self.env,
153 CATCH.as_ptr().cast(),
154 5,
155 Some(raw_promise_catch_callback::<E, U, Callback>),
156 rust_cb.cast(),
157 &mut catch_callback,
158 )
159 },
160 "Create catch function for PromiseRaw failed"
161 )?;
162 let mut new_promise = ptr::null_mut();
163 check_status!(
164 unsafe {
165 sys::napi_call_function(
166 self.env,
167 self.inner,
168 catch_fn,
169 1,
170 [catch_callback].as_mut_ptr().cast(),
171 &mut new_promise,
172 )
173 },
174 "Call the PromiseRaw::catch failed"
175 )?;
176
177 check_status!(
180 unsafe {
181 sys::napi_wrap(
182 self.env,
183 new_promise,
184 executed.cast(),
185 Some(promise_callback_finalizer::<E, U, Callback>),
186 rust_cb.cast(),
187 ptr::null_mut(),
188 )
189 },
190 "Wrap finalizer for PromiseRaw failed"
191 )?;
192
193 Ok(PromiseRaw::<U> {
194 env: self.env,
195 inner: new_promise,
196 _phantom: &PhantomData,
197 })
198 }
199
200 pub fn finally<'finally, U, Callback>(&mut self, cb: Callback) -> Result<PromiseRaw<'env, T>>
202 where
203 U: ToNapiValue,
204 Callback: 'finally + FnOnce(Env) -> Result<U>,
205 {
206 let mut then_fn = ptr::null_mut();
207 const FINALLY: &[u8; 8] = b"finally\0";
208
209 check_status!(unsafe {
210 sys::napi_get_named_property(self.env, self.inner, FINALLY.as_ptr().cast(), &mut then_fn)
211 })?;
212 let mut then_callback = ptr::null_mut();
213 let rust_cb = Box::into_raw(Box::new(cb));
214 check_status!(
215 unsafe {
216 sys::napi_create_function(
217 self.env,
218 FINALLY.as_ptr().cast(),
219 7,
220 Some(raw_promise_finally_callback::<U, Callback>),
221 rust_cb.cast(),
222 &mut then_callback,
223 )
224 },
225 "Create then function for PromiseRaw failed"
226 )?;
227 let mut new_promise = ptr::null_mut();
228 check_status!(
229 unsafe {
230 sys::napi_call_function(
231 self.env,
232 self.inner,
233 then_fn,
234 1,
235 [then_callback].as_ptr(),
236 &mut new_promise,
237 )
238 },
239 "Call then callback on PromiseRaw failed"
240 )?;
241
242 Ok(Self {
243 env: self.env,
244 inner: new_promise,
245 _phantom: &PhantomData,
246 })
247 }
248
249 #[cfg(all(feature = "napi4", feature = "tokio_rt"))]
250 pub fn into_sendable_promise(self) -> Result<Promise<T>> {
254 unsafe { Promise::from_napi_value(self.env, self.inner) }
255 }
256}
257
258pub(crate) fn validate_promise(
259 env: napi_sys::napi_env,
260 napi_val: napi_sys::napi_value,
261) -> Result<sys::napi_value> {
262 let mut is_promise = false;
263 check_status!(
264 unsafe { crate::sys::napi_is_promise(env, napi_val, &mut is_promise) },
265 "Failed to check if value is promise"
266 )?;
267 if !is_promise {
268 let mut deferred = ptr::null_mut();
269 let mut promise = ptr::null_mut();
270 check_status!(
271 unsafe { crate::sys::napi_create_promise(env, &mut deferred, &mut promise) },
272 "Failed to create promise"
273 )?;
274 const INVALID_ARG: &[u8; 11] = b"InvalidArg\0";
275 let mut err = ptr::null_mut();
276 let mut code = ptr::null_mut();
277 let mut message = ptr::null_mut();
278 check_status!(
279 unsafe {
280 crate::sys::napi_create_string_utf8(env, INVALID_ARG.as_ptr().cast(), 10, &mut code)
281 },
282 "Failed to create error message"
283 )?;
284 check_status!(
285 unsafe {
286 crate::sys::napi_create_string_utf8(
287 env,
288 c"Expected Promise object".as_ptr().cast(),
289 23,
290 &mut message,
291 )
292 },
293 "Failed to create error message"
294 )?;
295 check_status!(
296 unsafe { crate::sys::napi_create_error(env, code, message, &mut err) },
297 "Failed to create rejected error"
298 )?;
299 check_status!(
300 unsafe { crate::sys::napi_reject_deferred(env, deferred, err) },
301 "Failed to reject promise in validate"
302 )?;
303 return Ok(promise);
304 }
305 Ok(ptr::null_mut())
306}
307
308unsafe extern "C" fn raw_promise_then_callback<T, U, Cb>(
309 env: sys::napi_env,
310 cbinfo: sys::napi_callback_info,
311) -> sys::napi_value
312where
313 T: FromNapiValue,
314 U: ToNapiValue,
315 Cb: FnOnce(CallbackContext<T>) -> Result<U>,
316{
317 handle_then_callback::<T, U, Cb>(env, cbinfo)
318 .unwrap_or_else(|err| throw_error(env, err, "Error in Promise.then"))
319}
320
321#[inline]
322fn handle_then_callback<T, U, Cb>(
323 env: sys::napi_env,
324 cbinfo: sys::napi_callback_info,
325) -> Result<sys::napi_value>
326where
327 T: FromNapiValue,
328 U: ToNapiValue,
329 Cb: FnOnce(CallbackContext<T>) -> Result<U>,
330{
331 let mut callback_values = [ptr::null_mut()];
332 let mut rust_cb = ptr::null_mut();
333 check_status!(
334 unsafe {
335 sys::napi_get_cb_info(
336 env,
337 cbinfo,
338 &mut 1,
339 callback_values.as_mut_ptr(),
340 ptr::null_mut(),
341 &mut rust_cb,
342 )
343 },
344 "Get callback info from then callback failed"
345 )?;
346 let then_value: T = unsafe { FromNapiValue::from_napi_value(env, callback_values[0]) }?;
347 let cb: Box<(Cb, *mut bool)> = unsafe { Box::from_raw(rust_cb.cast()) };
348 let executed = unsafe { Box::leak(Box::from_raw(cb.1)) };
349 *executed = true;
350
351 unsafe {
352 U::to_napi_value(
353 env,
354 cb.0(CallbackContext {
355 env: Env(env),
356 value: then_value,
357 })?,
358 )
359 }
360}
361
362unsafe extern "C" fn raw_promise_catch_callback<E, U, Cb>(
363 env: sys::napi_env,
364 cbinfo: sys::napi_callback_info,
365) -> sys::napi_value
366where
367 E: FromNapiValue,
368 U: ToNapiValue,
369 Cb: FnOnce(CallbackContext<E>) -> Result<U>,
370{
371 handle_catch_callback::<E, U, Cb>(env, cbinfo)
372 .unwrap_or_else(|err| throw_error(env, err, "Error in Promise.catch"))
373}
374
375#[inline(always)]
376fn handle_catch_callback<E, U, Cb>(
377 env: sys::napi_env,
378 cbinfo: sys::napi_callback_info,
379) -> Result<sys::napi_value>
380where
381 E: FromNapiValue,
382 U: ToNapiValue,
383 Cb: FnOnce(CallbackContext<E>) -> Result<U>,
384{
385 let mut callback_values = [ptr::null_mut(); 1];
386 let mut rust_cb = ptr::null_mut();
387 check_status!(
388 unsafe {
389 sys::napi_get_cb_info(
390 env,
391 cbinfo,
392 &mut 1,
393 callback_values.as_mut_ptr(),
394 ptr::null_mut(),
395 &mut rust_cb,
396 )
397 },
398 "Get callback info from catch callback failed"
399 )?;
400 let catch_value: E = unsafe { FromNapiValue::from_napi_value(env, callback_values[0]) }?;
401 let cb: Box<(Cb, *mut bool)> = unsafe { Box::from_raw(rust_cb.cast()) };
402
403 let executed = unsafe { Box::leak(Box::from_raw(cb.1)) };
404 *executed = true;
405
406 unsafe {
407 U::to_napi_value(
408 env,
409 cb.0(CallbackContext {
410 env: Env(env),
411 value: catch_value,
412 })?,
413 )
414 }
415}
416
417unsafe extern "C" fn raw_promise_finally_callback<U, Cb>(
418 env: sys::napi_env,
419 cbinfo: sys::napi_callback_info,
420) -> sys::napi_value
421where
422 U: ToNapiValue,
423 Cb: FnOnce(Env) -> Result<U>,
424{
425 handle_finally_callback::<U, Cb>(env, cbinfo)
426 .unwrap_or_else(|err| throw_error(env, err, "Error in Promise.finally"))
427}
428
429#[inline(always)]
430fn handle_finally_callback<U, Cb>(
431 env: sys::napi_env,
432 cbinfo: sys::napi_callback_info,
433) -> Result<sys::napi_value>
434where
435 U: ToNapiValue,
436 Cb: FnOnce(Env) -> Result<U>,
437{
438 let mut rust_cb = ptr::null_mut();
439 check_status!(
440 unsafe {
441 sys::napi_get_cb_info(
442 env,
443 cbinfo,
444 &mut 0,
445 ptr::null_mut(),
446 ptr::null_mut(),
447 &mut rust_cb,
448 )
449 },
450 "Get callback info from finally callback failed"
451 )?;
452 let cb: Box<Cb> = unsafe { Box::from_raw(rust_cb.cast()) };
453
454 unsafe { U::to_napi_value(env, cb(Env(env))?) }
455}
456
457pub struct CallbackContext<T> {
458 pub env: Env,
459 pub value: T,
460}
461
462impl<T: ToNapiValue> ToNapiValue for CallbackContext<T> {
463 unsafe fn to_napi_value(env: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
464 T::to_napi_value(env, val.value)
465 }
466}
467
468#[inline(never)]
469fn throw_error(env: sys::napi_env, err: Error, default_msg: &str) -> sys::napi_value {
470 const GENERIC_FAILURE: &str = "GenericFailure\0";
471 let code = if err.status.as_ref().is_empty() {
472 GENERIC_FAILURE
473 } else {
474 err.status.as_ref()
475 };
476 let mut code_string = ptr::null_mut();
477 let msg = if err.reason.is_empty() {
478 default_msg
479 } else {
480 err.reason.as_ref()
481 };
482 let mut msg_string = ptr::null_mut();
483 let mut err = ptr::null_mut();
484 unsafe {
485 sys::napi_create_string_latin1(
486 env,
487 code.as_ptr().cast(),
488 code.len() as isize,
489 &mut code_string,
490 );
491 sys::napi_create_string_utf8(
492 env,
493 msg.as_ptr().cast(),
494 msg.len() as isize,
495 &mut msg_string,
496 );
497 sys::napi_create_error(env, code_string, msg_string, &mut err);
498 sys::napi_throw(env, err);
499 };
500 ptr::null_mut()
501}
502
503extern "C" fn promise_callback_finalizer<T, U, Cb>(
504 _env: sys::napi_env,
505 finalize_data: *mut c_void,
506 finalize_hint: *mut c_void,
507) where
508 T: FromNapiValue,
509 U: ToNapiValue,
510 Cb: FnOnce(CallbackContext<T>) -> Result<U>,
511{
512 if !unsafe { *Box::from_raw(finalize_data.cast()) } {
513 drop(unsafe { Box::from_raw(finalize_hint.cast::<Cb>()) });
514 }
515}