quickjs_rusty/value/
promise.rs

1use std::ops::Deref;
2
3use libquickjs_ng_sys as q;
4
5use crate::utils::ensure_no_excpetion;
6use crate::{Context, ExecutionError, JsFunction, ValueError};
7
8use super::OwnedJsValue;
9
10#[derive(Debug, Clone, PartialEq)]
11pub struct OwnedJsPromise {
12    value: OwnedJsValue,
13}
14
15impl OwnedJsPromise {
16    pub fn try_from_value(value: OwnedJsValue) -> Result<Self, ValueError> {
17        if !value.is_promise() {
18            Err(ValueError::Internal("Expected an promise".into()))
19        } else {
20            Ok(Self { value })
21        }
22    }
23
24    pub fn into_value(self) -> OwnedJsValue {
25        self.value
26    }
27
28    pub fn state(&self) -> PromiseState {
29        let state = unsafe { q::JS_PromiseState(self.value.context(), self.value.value) };
30        match state {
31            q::JSPromiseStateEnum_JS_PROMISE_PENDING => PromiseState::Pending,
32            q::JSPromiseStateEnum_JS_PROMISE_FULFILLED => PromiseState::Fulfilled,
33            q::JSPromiseStateEnum_JS_PROMISE_REJECTED => PromiseState::Rejected,
34            _ => unreachable!(),
35        }
36    }
37
38    /// Returns the result of the promise if the promise's state is in the FULFILLED or REJECTED state,
39    /// otherwise returns Undefined.
40    pub fn result(&self) -> OwnedJsValue {
41        let result = unsafe { q::JS_PromiseResult(self.value.context(), self.value.value) };
42        OwnedJsValue::new(self.value.context(), result)
43    }
44
45    pub fn then(&self, on_fulfilled: &OwnedJsValue) -> Result<OwnedJsPromise, ExecutionError> {
46        let new_promise = unsafe {
47            q::JS_Ext_PromiseThen(self.value.context(), self.value.value, on_fulfilled.value)
48        };
49
50        let new_promise = OwnedJsValue::new(self.value.context(), new_promise);
51
52        ensure_no_excpetion(self.value.context())?;
53
54        Ok(OwnedJsPromise::try_from_value(new_promise)?)
55    }
56
57    pub fn then2(
58        &self,
59        on_fulfilled: &OwnedJsValue,
60        on_rejected: &OwnedJsValue,
61    ) -> Result<OwnedJsPromise, ExecutionError> {
62        let new_promise = unsafe {
63            q::JS_Ext_PromiseThen2(
64                self.value.context(),
65                self.value.value,
66                on_fulfilled.value,
67                on_rejected.value,
68            )
69        };
70
71        let new_promise = OwnedJsValue::new(self.value.context(), new_promise);
72
73        ensure_no_excpetion(self.value.context())?;
74
75        Ok(OwnedJsPromise::try_from_value(new_promise)?)
76    }
77
78    pub fn catch(&self, on_rejected: &OwnedJsValue) -> Result<OwnedJsPromise, ExecutionError> {
79        let new_promise = unsafe {
80            q::JS_Ext_PromiseCatch(self.value.context(), self.value.value, on_rejected.value)
81        };
82
83        let new_promise = OwnedJsValue::new(self.value.context(), new_promise);
84
85        ensure_no_excpetion(self.value.context())?;
86
87        Ok(OwnedJsPromise::try_from_value(new_promise)?)
88    }
89
90    pub fn finally(&self, on_finally: &OwnedJsValue) -> Result<OwnedJsPromise, ExecutionError> {
91        let new_promise = unsafe {
92            q::JS_Ext_PromiseFinally(self.value.context(), self.value.value, on_finally.value)
93        };
94
95        let new_promise = OwnedJsValue::new(self.value.context(), new_promise);
96
97        ensure_no_excpetion(self.value.context())?;
98
99        Ok(OwnedJsPromise::try_from_value(new_promise)?)
100    }
101
102    pub fn resolve(
103        context: &Context,
104        value: &OwnedJsValue,
105    ) -> Result<OwnedJsPromise, ExecutionError> {
106        let promise = unsafe { q::JS_Ext_PromiseResolve(context.context, value.value) };
107        let promise = OwnedJsValue::new(context.context, promise);
108
109        ensure_no_excpetion(context.context)?;
110
111        Ok(OwnedJsPromise::try_from_value(promise)?)
112    }
113
114    pub fn reject(
115        context: &Context,
116        value: &OwnedJsValue,
117    ) -> Result<OwnedJsPromise, ExecutionError> {
118        let promise = unsafe { q::JS_Ext_PromiseReject(context.context, value.value) };
119        let promise = OwnedJsValue::new(context.context, promise);
120
121        ensure_no_excpetion(context.context)?;
122
123        Ok(OwnedJsPromise::try_from_value(promise)?)
124    }
125
126    pub fn all(
127        context: &Context,
128        values: impl IntoIterator<Item = OwnedJsPromise>,
129    ) -> Result<OwnedJsPromise, ExecutionError> {
130        let iterable: OwnedJsValue =
131            (context.context, values.into_iter().collect::<Vec<_>>()).into();
132
133        let promise = unsafe { q::JS_Ext_PromiseAll(context.context, iterable.value) };
134        let promise = OwnedJsValue::new(context.context, promise);
135
136        ensure_no_excpetion(context.context)?;
137
138        Ok(OwnedJsPromise::try_from_value(promise)?)
139    }
140
141    pub fn all_settled(
142        context: &Context,
143        values: impl IntoIterator<Item = OwnedJsPromise>,
144    ) -> Result<OwnedJsPromise, ExecutionError> {
145        let iterable: OwnedJsValue =
146            (context.context, values.into_iter().collect::<Vec<_>>()).into();
147
148        let promise = unsafe { q::JS_Ext_PromiseAllSettled(context.context, iterable.value) };
149        let promise = OwnedJsValue::new(context.context, promise);
150
151        ensure_no_excpetion(context.context)?;
152
153        Ok(OwnedJsPromise::try_from_value(promise)?)
154    }
155
156    pub fn race(
157        context: &Context,
158        values: impl IntoIterator<Item = OwnedJsPromise>,
159    ) -> Result<OwnedJsPromise, ExecutionError> {
160        let iterable: OwnedJsValue =
161            (context.context, values.into_iter().collect::<Vec<_>>()).into();
162
163        let promise = unsafe { q::JS_Ext_PromiseRace(context.context, iterable.value) };
164        let promise = OwnedJsValue::new(context.context, promise);
165
166        ensure_no_excpetion(context.context)?;
167
168        Ok(OwnedJsPromise::try_from_value(promise)?)
169    }
170
171    pub fn any(
172        context: &Context,
173        values: impl IntoIterator<Item = OwnedJsPromise>,
174    ) -> Result<OwnedJsPromise, ExecutionError> {
175        let iterable: OwnedJsValue =
176            (context.context, values.into_iter().collect::<Vec<_>>()).into();
177
178        let promise = unsafe { q::JS_Ext_PromiseAny(context.context, iterable.value) };
179        let promise = OwnedJsValue::new(context.context, promise);
180
181        ensure_no_excpetion(context.context)?;
182
183        Ok(OwnedJsPromise::try_from_value(promise)?)
184    }
185
186    pub fn with_resolvers(
187        context: &Context,
188    ) -> Result<(OwnedJsPromise, JsFunction, JsFunction), ExecutionError> {
189        let obj = unsafe { q::JS_Ext_PromiseWithResolvers(context.context) };
190        let obj = OwnedJsValue::new(context.context, obj);
191
192        ensure_no_excpetion(context.context)?;
193
194        let obj = obj.try_into_object()?;
195
196        // use .unwrap() here because the fields are guaranteed to be there
197        let promise = obj.property("promise")?.unwrap().try_into_promise()?;
198        let resolve = obj.property("resolve")?.unwrap().try_into_function()?;
199        let reject = obj.property("reject")?.unwrap().try_into_function()?;
200
201        Ok((promise, resolve, reject))
202    }
203}
204
205impl Deref for OwnedJsPromise {
206    type Target = OwnedJsValue;
207
208    fn deref(&self) -> &Self::Target {
209        &self.value
210    }
211}
212
213#[derive(Debug, Clone, Copy)]
214pub enum PromiseState {
215    Pending,
216    Fulfilled,
217    Rejected,
218}