11use crate :: { convert, vm_class:: AccessibleVM , wasm_builtins:: window} ;
2- use futures:: { future , Future } ;
2+ use futures:: Future ;
33use js_sys:: Promise ;
44use rustpython_vm:: obj:: { objint, objstr} ;
5- use rustpython_vm:: pyobject:: { PyContext , PyFuncArgs , PyObjectRef , PyResult , TypeProtocol } ;
6- use rustpython_vm:: VirtualMachine ;
5+ use rustpython_vm:: pyobject:: {
6+ PyContext , PyFuncArgs , PyObject , PyObjectPayload , PyObjectRef , PyResult , TypeProtocol ,
7+ } ;
8+ use rustpython_vm:: { import:: import, VirtualMachine } ;
9+ use std:: path:: PathBuf ;
710use wasm_bindgen:: { prelude:: * , JsCast } ;
811use wasm_bindgen_futures:: { future_to_promise, JsFuture } ;
912
@@ -32,15 +35,10 @@ impl FetchResponseFormat {
3235}
3336
3437fn browser_fetch ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
35- arg_check ! (
36- vm,
37- args,
38- required = [
39- ( url, Some ( vm. ctx. str_type( ) ) ) ,
40- ( handler, Some ( vm. ctx. function_type( ) ) )
41- ] ,
42- optional = [ ( reject_handler, Some ( vm. ctx. function_type( ) ) ) ]
43- ) ;
38+ arg_check ! ( vm, args, required = [ ( url, Some ( vm. ctx. str_type( ) ) ) ] ) ;
39+
40+ let promise_type = import_promise_type ( vm) ?;
41+
4442 let response_format =
4543 args. get_optional_kwarg_with_type ( "response_format" , vm. ctx . str_type ( ) , vm) ?;
4644 let method = args. get_optional_kwarg_with_type ( "method" , vm. ctx . str_type ( ) , vm) ?;
@@ -87,42 +85,16 @@ fn browser_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
8785 let window = window ( ) ;
8886 let request_prom = window. fetch_with_request ( & request) ;
8987
90- let handler = handler. clone ( ) ;
91- let reject_handler = reject_handler. cloned ( ) ;
92-
93- let acc_vm = AccessibleVM :: from_vm ( vm) ;
94-
9588 let future = JsFuture :: from ( request_prom)
9689 . and_then ( move |val| {
9790 let response = val
9891 . dyn_into :: < web_sys:: Response > ( )
9992 . expect ( "val to be of type Response" ) ;
10093 response_format. get_response ( & response)
10194 } )
102- . and_then ( JsFuture :: from)
103- . then ( move |val| {
104- let vm = & mut acc_vm
105- . upgrade ( )
106- . expect ( "that the VM *not* be destroyed while promise is being resolved" ) ;
107- match val {
108- Ok ( val) => {
109- let val = convert:: js_to_py ( vm, val) ;
110- let args = PyFuncArgs :: new ( vec ! [ val] , vec ! [ ] ) ;
111- let _ = vm. invoke ( handler, args) ;
112- }
113- Err ( val) => {
114- if let Some ( reject_handler) = reject_handler {
115- let val = convert:: js_to_py ( vm, val) ;
116- let args = PyFuncArgs :: new ( vec ! [ val] , vec ! [ ] ) ;
117- let _ = vm. invoke ( reject_handler, args) ;
118- }
119- }
120- }
121- future:: ok ( JsValue :: UNDEFINED )
122- } ) ;
123- future_to_promise ( future) ;
95+ . and_then ( JsFuture :: from) ;
12496
125- Ok ( vm . get_none ( ) )
97+ Ok ( PyPromise :: new ( promise_type , future_to_promise ( future ) ) )
12698}
12799
128100fn browser_request_animation_frame ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
@@ -167,7 +139,7 @@ fn browser_request_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) ->
167139fn browser_cancel_animation_frame ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
168140 arg_check ! ( vm, args, required = [ ( id, Some ( vm. ctx. int_type( ) ) ) ] ) ;
169141
170- // fine because
142+ // questionable, but it's probably fine
171143 let id = objint:: get_value ( id)
172144 . to_string ( )
173145 . parse ( )
@@ -180,13 +152,135 @@ fn browser_cancel_animation_frame(vm: &mut VirtualMachine, args: PyFuncArgs) ->
180152 Ok ( vm. get_none ( ) )
181153}
182154
155+ pub struct PyPromise {
156+ value : Promise ,
157+ }
158+
159+ impl PyPromise {
160+ pub fn new ( promise_type : PyObjectRef , value : Promise ) -> PyObjectRef {
161+ PyObject :: new (
162+ PyObjectPayload :: AnyRustValue {
163+ value : Box :: new ( PyPromise { value } ) ,
164+ } ,
165+ promise_type,
166+ )
167+ }
168+ }
169+
170+ fn get_value ( obj : & PyObjectRef ) -> Promise {
171+ if let PyObjectPayload :: AnyRustValue { value } = & obj. payload {
172+ if let Some ( promise) = value. downcast_ref :: < PyPromise > ( ) {
173+ return promise. value . clone ( ) ;
174+ }
175+ }
176+ panic ! ( "Inner error getting promise" )
177+ }
178+
179+ fn import_promise_type ( vm : & mut VirtualMachine ) -> PyResult {
180+ import (
181+ vm,
182+ PathBuf :: default ( ) ,
183+ BROWSER_NAME ,
184+ & Some ( "Promise" . into ( ) ) ,
185+ )
186+ }
187+
188+ fn promise_then ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
189+ let promise_type = import_promise_type ( vm) ?;
190+ arg_check ! (
191+ vm,
192+ args,
193+ required = [
194+ ( zelf, Some ( promise_type. clone( ) ) ) ,
195+ ( on_fulfill, Some ( vm. ctx. function_type( ) ) )
196+ ] ,
197+ optional = [ ( on_reject, Some ( vm. ctx. function_type( ) ) ) ]
198+ ) ;
199+
200+ let on_fulfill = on_fulfill. clone ( ) ;
201+ let on_reject = on_reject. cloned ( ) ;
202+
203+ let acc_vm = AccessibleVM :: from_vm ( vm) ;
204+
205+ let promise = get_value ( zelf) ;
206+
207+ let ret_future = JsFuture :: from ( promise) . then ( move |res| {
208+ let vm = & mut acc_vm
209+ . upgrade ( )
210+ . expect ( "that the vm is valid when the promise resolves" ) ;
211+ let ret = match res {
212+ Ok ( val) => {
213+ let val = convert:: js_to_py ( vm, val) ;
214+ vm. invoke ( on_fulfill, PyFuncArgs :: new ( vec ! [ val] , vec ! [ ] ) )
215+ }
216+ Err ( err) => {
217+ if let Some ( on_reject) = on_reject {
218+ let err = convert:: js_to_py ( vm, err) ;
219+ vm. invoke ( on_reject, PyFuncArgs :: new ( vec ! [ err] , vec ! [ ] ) )
220+ } else {
221+ return Err ( err) ;
222+ }
223+ }
224+ } ;
225+ ret. map ( |val| convert:: py_to_js ( vm, val) )
226+ . map_err ( |err| convert:: py_to_js ( vm, err) )
227+ } ) ;
228+
229+ let ret_promise = future_to_promise ( ret_future) ;
230+
231+ Ok ( PyPromise :: new ( promise_type, ret_promise) )
232+ }
233+
234+ fn promise_catch ( vm : & mut VirtualMachine , args : PyFuncArgs ) -> PyResult {
235+ let promise_type = import_promise_type ( vm) ?;
236+ arg_check ! (
237+ vm,
238+ args,
239+ required = [
240+ ( zelf, Some ( promise_type. clone( ) ) ) ,
241+ ( on_reject, Some ( vm. ctx. function_type( ) ) )
242+ ]
243+ ) ;
244+
245+ let on_reject = on_reject. clone ( ) ;
246+
247+ let acc_vm = AccessibleVM :: from_vm ( vm) ;
248+
249+ let promise = get_value ( zelf) ;
250+
251+ let ret_future = JsFuture :: from ( promise) . then ( move |res| match res {
252+ Ok ( val) => Ok ( val) ,
253+ Err ( err) => {
254+ let vm = & mut acc_vm
255+ . upgrade ( )
256+ . expect ( "that the vm is valid when the promise resolves" ) ;
257+ let err = convert:: js_to_py ( vm, err) ;
258+ vm. invoke ( on_reject, PyFuncArgs :: new ( vec ! [ err] , vec ! [ ] ) )
259+ . map ( |val| convert:: py_to_js ( vm, val) )
260+ . map_err ( |err| convert:: py_to_js ( vm, err) )
261+ }
262+ } ) ;
263+
264+ let ret_promise = future_to_promise ( ret_future) ;
265+
266+ Ok ( PyPromise :: new ( promise_type, ret_promise) )
267+ }
268+
183269const BROWSER_NAME : & str = "browser" ;
184270
185271pub fn mk_module ( ctx : & PyContext ) -> PyObjectRef {
272+ let promise = {
273+ let promise = ctx. new_class ( "Promise" , ctx. object ( ) ) ;
274+ ctx. set_attr ( & promise, "then" , ctx. new_rustfunc ( promise_then) ) ;
275+ ctx. set_attr ( & promise, "catch" , ctx. new_rustfunc ( promise_catch) ) ;
276+ promise
277+ } ;
278+
186279 py_module ! ( ctx, BROWSER_NAME , {
187280 "fetch" => ctx. new_rustfunc( browser_fetch) ,
188281 "request_animation_frame" => ctx. new_rustfunc( browser_request_animation_frame) ,
189282 "cancel_animation_frame" => ctx. new_rustfunc( browser_cancel_animation_frame) ,
283+ "Promise" => promise,
190284 } )
191285}
192286
0 commit comments