jkb0o / pecs

Asynchronous operations for Bevy Engine
Apache License 2.0
64 stars 7 forks source link

Cannot specify a dynamic URL for http queries. #6

Closed joverwey closed 1 year ago

joverwey commented 1 year ago

In the simple example, one can specify the following: state.asyn().http().get("https://bevyengine.org")

but not

state.asyn().http().get(url) if url is defined in the outer scope or if it depends on anything in the outer scope.

The error is:

examples/simple.rs:25:15
   |
25 |           .then(asyn!(state => {
   |  _______________^
26 | |             info!("How large is is the Bevy main web page?");
27 | |             state.asyn().http().get(url)
28 | |         }))
   | |__________^ expected fn pointer, found closure
   |
   = note: expected fn pointer `for<'a, 'b> fn(In<(PromiseState<f32>, ())>, StaticSystemParam<'a, 'b, ()>) -> StatefulRequest<f32>

If you wanted to do a REST query where you have to specify some dynamic parameters, this would not compile. Is there any workaround?

jkb0o commented 1 year ago

Oh. Sorry, missed this one.

You can

This is how you can bypass arguments from your systems to url with send():

fn make_rest_call(mut commands: Commands) {
    // you can get query args from events, resources, whatever you want
    let q = "jkbo";
    let url = format!("https://api.github.com/search/users?q={q}");
    commands.add(asyn::http::get(url).send().then(asyn!(|_, r| {
        match r {
            Ok(r) => info!("{0}", r.text().unwrap()),
            Err(e) => error!("{e}")
        }
    })));
}

There is how it is possible to create higher level methods for calling rest api:

use bevy::prelude::*;
use pecs::prelude::*;
use serde::Deserialize;
use serde_json;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(PecsPlugin)
        .add_systems(Startup, make_rest_call)
        .run();
}

#[derive(Deserialize)]
struct FoundUsers {
    items: Vec<User>,
}

#[derive(Deserialize)]
struct User {
    id: usize,
    login: String,
}

// make search users request to GitHub, return promise resolved 
// with found Users
fn find_github_users(q: &'static str) -> Promise<(), Result<Vec<User>, String>> {
    let url = format!("https://api.github.com/search/users?q={q}");
    asyn::http::get(url).send().then(asyn!(_, result => {
        Promise::resolve(result.and_then(|resp| {
            let text = resp.text().unwrap();
            if !resp.ok {
                Err(format!("[{} {}] {text}", resp.status, resp.status_text))
            } else {
                serde_json::from_str::<FoundUsers>(text)
                    .map_err(|e| e.to_string())
                    .map(|found| found.items)
            }
        }))
    }))
}

fn make_rest_call(mut commands: Commands) {
    commands.add(find_github_users("jkbo").then(asyn!(_, resp => {
        match resp {
            Err(e) => error!("{e}"),
            Ok(items) => for user in items.iter() {
                info!("{} # {}", user.id, user.login)
            }
        }
        asyn::app::exit()
    })));
}