napi_h/bindgen_runtime/js_values/
promise.rs1use std::ffi::CStr;
2use std::future;
3use std::pin::Pin;
4use std::ptr;
5use std::sync::{
6 atomic::{AtomicBool, Ordering},
7 Arc,
8};
9use std::task::{Context, Poll};
10
11use tokio::sync::oneshot::{channel, Receiver, Sender};
12
13use crate::{check_status, sys, Error, JsUnknown, NapiValue, Result, Status};
14
15use super::{FromNapiValue, TypeName, ValidateNapiValue};
16
17pub struct Promise<T: FromNapiValue> {
18 value: Pin<Box<Receiver<*mut Result<T>>>>,
19 aborted: Arc<AtomicBool>,
20}
21
22impl<T: FromNapiValue> Drop for Promise<T> {
23 fn drop(&mut self) {
24 self.aborted.store(true, Ordering::SeqCst);
25 }
26}
27
28impl<T: FromNapiValue> TypeName for Promise<T> {
29 fn type_name() -> &'static str {
30 "Promise"
31 }
32
33 fn value_type() -> crate::ValueType {
34 crate::ValueType::Object
35 }
36}
37
38impl<T: FromNapiValue> ValidateNapiValue for Promise<T> {
39 unsafe fn validate(
40 env: crate::sys::napi_env,
41 napi_val: crate::sys::napi_value,
42 ) -> Result<sys::napi_value> {
43 let mut is_promise = false;
44 check_status!(
45 unsafe { crate::sys::napi_is_promise(env, napi_val, &mut is_promise) },
46 "Failed to check if value is promise"
47 )?;
48 if !is_promise {
49 let mut deferred = ptr::null_mut();
50 let mut promise = ptr::null_mut();
51 check_status!(
52 unsafe { crate::sys::napi_create_promise(env, &mut deferred, &mut promise) },
53 "Failed to create promise"
54 )?;
55 let mut err = ptr::null_mut();
56 let mut code = ptr::null_mut();
57 let mut message = ptr::null_mut();
58 check_status!(
59 unsafe {
60 crate::sys::napi_create_string_utf8(
61 env,
62 CStr::from_bytes_with_nul_unchecked(b"InvalidArg\0").as_ptr(),
63 10,
64 &mut code,
65 )
66 },
67 "Failed to create error message"
68 )?;
69 check_status!(
70 unsafe {
71 crate::sys::napi_create_string_utf8(
72 env,
73 CStr::from_bytes_with_nul_unchecked(b"Expected Promise object\0").as_ptr(),
74 23,
75 &mut message,
76 )
77 },
78 "Failed to create error message"
79 )?;
80 check_status!(
81 unsafe { crate::sys::napi_create_error(env, code, message, &mut err) },
82 "Failed to create rejected error"
83 )?;
84 check_status!(
85 unsafe { crate::sys::napi_reject_deferred(env, deferred, err) },
86 "Failed to reject promise in validate"
87 )?;
88 return Ok(promise);
89 }
90 Ok(ptr::null_mut())
91 }
92}
93
94unsafe impl<T: FromNapiValue + Send> Send for Promise<T> {}
95
96impl<T: FromNapiValue> FromNapiValue for Promise<T> {
97 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
98 let mut then = ptr::null_mut();
99 let then_c_string = unsafe { CStr::from_bytes_with_nul_unchecked(b"then\0") };
100 check_status!(
101 unsafe { sys::napi_get_named_property(env, napi_val, then_c_string.as_ptr(), &mut then) },
102 "Failed to get then function"
103 )?;
104 let mut promise_after_then = ptr::null_mut();
105 let mut then_js_cb = ptr::null_mut();
106 let (tx, rx) = channel();
107 let aborted = Arc::new(AtomicBool::new(false));
108 let tx_ptr = Box::into_raw(Box::new((tx, aborted.clone())));
109 check_status!(
110 unsafe {
111 sys::napi_create_function(
112 env,
113 then_c_string.as_ptr(),
114 4,
115 Some(then_callback::<T>),
116 tx_ptr.cast(),
117 &mut then_js_cb,
118 )
119 },
120 "Failed to create then callback"
121 )?;
122 check_status!(
123 unsafe {
124 sys::napi_call_function(
125 env,
126 napi_val,
127 then,
128 1,
129 [then_js_cb].as_ptr(),
130 &mut promise_after_then,
131 )
132 },
133 "Failed to call then method"
134 )?;
135 let mut catch = ptr::null_mut();
136 let catch_c_string = unsafe { CStr::from_bytes_with_nul_unchecked(b"catch\0") };
137 check_status!(
138 unsafe {
139 sys::napi_get_named_property(env, promise_after_then, catch_c_string.as_ptr(), &mut catch)
140 },
141 "Failed to get then function"
142 )?;
143 let mut catch_js_cb = ptr::null_mut();
144 check_status!(
145 unsafe {
146 sys::napi_create_function(
147 env,
148 catch_c_string.as_ptr(),
149 5,
150 Some(catch_callback::<T>),
151 tx_ptr.cast(),
152 &mut catch_js_cb,
153 )
154 },
155 "Failed to create catch callback"
156 )?;
157 check_status!(
158 unsafe {
159 sys::napi_call_function(
160 env,
161 promise_after_then,
162 catch,
163 1,
164 [catch_js_cb].as_ptr(),
165 ptr::null_mut(),
166 )
167 },
168 "Failed to call catch method"
169 )?;
170 Ok(Promise {
171 value: Box::pin(rx),
172 aborted,
173 })
174 }
175}
176
177impl<T: FromNapiValue> future::Future for Promise<T> {
178 type Output = Result<T>;
179
180 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
181 match self.value.as_mut().poll(cx) {
182 Poll::Pending => Poll::Pending,
183 Poll::Ready(v) => Poll::Ready(
184 v.map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))
185 .and_then(|v| unsafe { *Box::from_raw(v) }.map_err(Error::from)),
186 ),
187 }
188 }
189}
190
191unsafe extern "C" fn then_callback<T: FromNapiValue>(
192 env: sys::napi_env,
193 info: sys::napi_callback_info,
194) -> sys::napi_value {
195 let mut data = ptr::null_mut();
196 let mut resolved_value: [sys::napi_value; 1] = [ptr::null_mut()];
197 let mut this = ptr::null_mut();
198 let get_cb_status = unsafe {
199 sys::napi_get_cb_info(
200 env,
201 info,
202 &mut 1,
203 resolved_value.as_mut_ptr(),
204 &mut this,
205 &mut data,
206 )
207 };
208 debug_assert!(
209 get_cb_status == sys::Status::napi_ok,
210 "Get callback info from Promise::then failed"
211 );
212 let (sender, aborted) =
213 *unsafe { Box::from_raw(data as *mut (Sender<*mut Result<T>>, Arc<AtomicBool>)) };
214 if aborted.load(Ordering::SeqCst) {
215 return this;
216 }
217 let resolve_value_t = Box::new(unsafe { T::from_napi_value(env, resolved_value[0]) });
218 sender
219 .send(Box::into_raw(resolve_value_t))
220 .expect("Send Promise resolved value error");
221 this
222}
223
224unsafe extern "C" fn catch_callback<T: FromNapiValue>(
225 env: sys::napi_env,
226 info: sys::napi_callback_info,
227) -> sys::napi_value {
228 let mut data = ptr::null_mut();
229 let mut rejected_value: [sys::napi_value; 1] = [ptr::null_mut()];
230 let mut this = ptr::null_mut();
231 let mut argc = 1;
232 let get_cb_status = unsafe {
233 sys::napi_get_cb_info(
234 env,
235 info,
236 &mut argc,
237 rejected_value.as_mut_ptr(),
238 &mut this,
239 &mut data,
240 )
241 };
242 debug_assert!(
243 get_cb_status == sys::Status::napi_ok,
244 "Get callback info from Promise::catch failed"
245 );
246 let rejected_value = rejected_value[0];
247 let (sender, aborted) =
248 *unsafe { Box::from_raw(data as *mut (Sender<*mut Result<T>>, Arc<AtomicBool>)) };
249 if aborted.load(Ordering::SeqCst) {
250 return this;
251 }
252 sender
253 .send(Box::into_raw(Box::new(Err(Error::from(unsafe {
254 JsUnknown::from_raw_unchecked(env, rejected_value)
255 })))))
256 .expect("Send Promise resolved value error");
257 this
258}