napi/bindgen_runtime/js_values/
promise.rs1use std::{
2 cell::Cell,
3 convert::identity,
4 future,
5 pin::Pin,
6 rc::Rc,
7 task::{Context, Poll},
8};
9
10use futures::channel::oneshot::{channel, Receiver};
11
12use crate::{sys, Error, Result, Status};
13
14use super::{CallbackContext, FromNapiValue, PromiseRaw, TypeName, Unknown, ValidateNapiValue};
15
16pub struct Promise<T: 'static + FromNapiValue> {
35 value: Pin<Box<Receiver<Result<T>>>>,
36}
37
38impl<T: FromNapiValue> TypeName for Promise<T> {
39 fn type_name() -> &'static str {
40 "Promise"
41 }
42
43 fn value_type() -> crate::ValueType {
44 crate::ValueType::Object
45 }
46}
47
48impl<T: FromNapiValue> ValidateNapiValue for Promise<T> {
49 unsafe fn validate(
50 env: crate::sys::napi_env,
51 napi_val: crate::sys::napi_value,
52 ) -> Result<sys::napi_value> {
53 use super::validate_promise;
54
55 validate_promise(env, napi_val)
56 }
57}
58
59unsafe impl<T: FromNapiValue + Send> Send for Promise<T> {}
60
61impl<T: FromNapiValue> FromNapiValue for Promise<T> {
62 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
63 let (tx, rx) = channel();
64 let promise_object = unsafe { PromiseRaw::<T>::from_napi_value(env, napi_val)? };
65 let tx_box = Rc::new(Cell::new(Some(tx)));
66 let tx_in_catch = tx_box.clone();
67 promise_object
68 .then(move |ctx| {
69 if let Some(sender) = tx_box.replace(None) {
70 let _ = sender.send(Ok(ctx.value));
72 }
73 Ok(())
74 })?
75 .catch(move |ctx: CallbackContext<Unknown>| {
76 if let Some(sender) = tx_in_catch.replace(None) {
77 let _ = sender.send(Err(ctx.value.into()));
79 }
80 Ok(())
81 })?;
82
83 Ok(Promise {
84 value: Box::pin(rx),
85 })
86 }
87}
88
89impl<T: FromNapiValue> future::Future for Promise<T> {
90 type Output = Result<T>;
91
92 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
93 match self.value.as_mut().poll(cx) {
94 Poll::Pending => Poll::Pending,
95 Poll::Ready(v) => Poll::Ready(
96 v.map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))
97 .and_then(identity),
98 ),
99 }
100 }
101}