use std::cell::Cell;
use std::convert::identity;
use std::future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::sync::oneshot::{channel, Receiver};
use crate::{sys, Error, Result, Status};
use super::{CallbackContext, FromNapiValue, PromiseRaw, TypeName, Unknown, ValidateNapiValue};
pub struct Promise<T: 'static + FromNapiValue> {
value: Pin<Box<Receiver<Result<T>>>>,
}
impl<T: FromNapiValue> TypeName for Promise<T> {
fn type_name() -> &'static str {
"Promise"
}
fn value_type() -> crate::ValueType {
crate::ValueType::Object
}
}
impl<T: FromNapiValue> ValidateNapiValue for Promise<T> {
unsafe fn validate(
env: crate::sys::napi_env,
napi_val: crate::sys::napi_value,
) -> Result<sys::napi_value> {
use super::validate_promise;
validate_promise(env, napi_val)
}
}
unsafe impl<T: FromNapiValue + Send> Send for Promise<T> {}
impl<T: FromNapiValue> FromNapiValue for Promise<T> {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
let (tx, rx) = channel();
let mut promise_object = unsafe { PromiseRaw::<T>::from_napi_value(env, napi_val)? };
let tx_box = Arc::new(Cell::new(Some(tx)));
let tx_in_catch = tx_box.clone();
promise_object
.then(move |ctx| {
if let Some(sender) = tx_box.replace(None) {
let _ = sender.send(Ok(ctx.value));
}
Ok(())
})?
.catch(move |ctx: CallbackContext<Unknown>| {
if let Some(sender) = tx_in_catch.replace(None) {
let _ = sender.send(Err(ctx.value.into()));
}
Ok(())
})?;
Ok(Promise {
value: Box::pin(rx),
})
}
}
impl<T: FromNapiValue> future::Future for Promise<T> {
type Output = Result<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.value.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(v) => Poll::Ready(
v.map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))
.and_then(identity),
),
}
}
}