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(Error::from_unknown_without_coercion(ctx.value)));
82 }
83 Ok(())
84 })?;
85
86 Ok(Promise {
87 value: Box::pin(rx),
88 })
89 }
90}
91
92impl<T: FromNapiValue> future::Future for Promise<T> {
93 type Output = Result<T>;
94
95 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
96 match self.value.as_mut().poll(cx) {
97 Poll::Pending => Poll::Pending,
98 Poll::Ready(v) => Poll::Ready(
99 v.map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))
100 .and_then(identity),
101 ),
102 }
103 }
104}