How do I get URL parameters? - rust

Something wrong with below code (I'm absolutely new in Rust...):
let function = move |req: &mut Request| -> IronResult<Response> {
let router = req.extensions.get::<Router>().expect("Unable to get router");
println!("router:{:?}", router);
let val = router.find("param").expect("param is required");
...
}
...
router.get("/page", function, "handler");
...
While executed (site/page?param=0), got below traces. Looks like 'router' is empty. What's is wrong ?
router:Params { map: {} }
thread '<unnamed>' panicked at 'param is required', src/main.rs:xx:xx
So, I decided to use params::{Params} and almost reached my goal, but...
let function = move |req: &mut Request| -> IronResult<Response> {
use params::{Params};
use crate::iron::Plugin;
let map = req.get_ref::<Params>().unwrap();
let val1 = map.find(&["param1"]).expect("param1 is required");
let val2 = map.find(&["param2"]).expect("param2 is required");
let cmd = format!("cmd={:?}{:?}\n", val1, val2);
println!("{}", cmd);
...
};
I get cmd="1600","100" instead of wanted cmd=1600,100. Can't use format!({}) due to compilation error:
error[E0277]: `params::Value` doesn't implement `std::fmt::Display`
`params::Value` cannot be formatted with the default formatter
Tried different string/number conversions but failed since val1, val2 are not strings but params::Value. Tried remove quotes from cmd string but also failed. Any idea of simple solution?

The params of the Router are those specified in the URL pattern e.g. if you define a route GET /:query then you'll get a query param storing the corresponding path segment.
The querystring you access via Request::url.

Related

How to properly handle a tokio::try_join! if one of the tasks panics and cleanly abort?

For the two async functions that I am passing to my try_join!(), let's say there's 3 ways that they could panic.
I'm trying to use a set_hook to catch the errors but I'm not sure how to do a match statement on the panics so I can display a custom error message for each of the ways that they can panic. It looks like set_hook takes a Box(Any) (?), so I was wondering if there was a way to check the type of Error. Basically I just don't want to do regex on the ErrString.
I'm also not sure what the best way to abort the runtime within each match branch. I'm currently using std::process::exit(0).
code looks like:
set_hook(Box::new(|panic_info| {
println!("Thread panicked! {}", panic_info);
// std::process::exit(0);
}));
let (result1, result2) = tokio::try_join!(func1, func2); // code that could panic
I want to be able to do something like
set_hook(Box::new(|panic_info| {
match panic_info {
panic_type_1 => { println!("X was invalid, please try using valid X") }
panic_type_2 => { println!("Y was invalid, please try using valid Y") }
panic_type_3 => { println!("Z was invalid, please try using valid Z") }
_ => { println!("Something else happened: {}", panic_info) }
}
}));
let (result1, result2) = tokio::try_join!(func1, func2); // code that could panic
Don't bother with set_hook. The future that tokio::task::spawn* returns resolves to a Result with a JoinError type, which has a [try_]into_panic to get the boxed object that was passed to panic.
The panic message is stored as a Box<dyn Any> which has tons of methods for downcasting it into various types.

Turning tokio_postgres client into a variable for reuse

I am trying to figure out a way to make my tokio_postgres client a variable that I can reuse in different parts of my app. Ideally, I'm trying to achieve something similar to the Prisma ORM in the Node world:
const prisma = new PrismaClient()
...
const user = await prisma.user.create({
data: {
name: 'Alice',
email: 'alice#prisma.io',
},
The code I have so far is:
async fn connect() -> Result<P::Client, PgError> {
// Connect to the database.
let (client, connection) =
tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
// The connection object performs the actual communication with the database,
// so spawn it off to run on its own.
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
// Now we can execute a simple statement that just returns its parameter.
let rows = client
.query("SELECT $1::TEXT", &[&"hello world"])
.await?;
// And then check that we got back the same string we sent over.
let value: &str = rows[0].get(0);
assert_eq!(value, "hello world");
return client;
}
However, I am getting the error:
expected type Result<tokio_postgres::Client, tokio_postgres::Error>
found struct tokio_postgres::Client
Any idea what I could be doing wrong here? I'm new to Rust and maybe I'm just bringing baggage from Node, but I haven't found any documentation on this and figured it would be good to have.

expected `()`, found opaque type for async function

I am following a guide for setting up a WebRTC data-channel with web-sys. I can copy and paste the code and it compiles correctly. The start() function is async which makes it possible to await a JsFuture inside the main scope, however I am trying to move this await to the onmessage_callback block instead. Just by adding this one line to the original implementation I have this:
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| {
let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();
Once I compile this I ofcourse get an error saying that awaiting the future desc is only possible inside an asynchronous context. I figured that I could add the async keyword when defining the FnMut function:
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| async { // <-- async
let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();
But when I compile this I get the following error:
error[E0271]: type mismatch resolving `<[closure#src/lib.rs:48:22: 57:14] as FnOnce<(MessageEvent,)>>::Output == ()`
--> src/lib.rs:48:13
|
48 | / Box::new(move |ev: MessageEvent| async {
49 | | let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
50 | | match ev.data().as_string() {
51 | | Some(message) => {
... |
56 | | }
57 | | }) as Box<dyn FnMut(MessageEvent)>,
| |______________^ expected `()`, found opaque type
|
= note: expected unit type `()`
found opaque type `impl Future<Output = [async output]>`
= note: required for the cast to the object type `dyn FnMut(MessageEvent)`
I am not sure how to proceed with this, I think the error is saying that the callback returns a future but it should be void instead.
How do I define an async callback?
You declare that your function will not return anything by saying
Box<dyn FnMut(MessageEvent)>
//which is the same thing as Box<dyn FnMut(MessageEvent)->()>
then in your callback, you actually return an async block back.
I am not familiar with the library in question but looking at the documentation you are not supposed to pass an async function.
if you must use async and if you have a runtime running already you can spawn a task inside a sync block.
But in javascript world you cant really wait for something if something is async you can only give it a callback (async/await keywords exists but all they do is to return you another promise)
So I believe you should be using the promise API here and provide a callback for the then method.
It is not pretty but this is how things get done in the js world and what you are facing here is called the callback hell in js which is a thing :D
Ps:
Javascript has a single threaded runtime and things get scheduled on a forever running loop called the event loop. Every time you pass a callback somewhere at some point it gets registered on the event loop and gets called eventually.
What's important to understand is that the calling part of it is not guaranteed and if you were to block event loop in one of your callbacks then next tick of the loop would never come therefore the entire runtime would lock up. this is the reason behind the design decisions that lead to the callback hell
Pps:
I don't really think you should bundle (if even its possible) a rust async runtime with your wasm binary since js has its own as I mentioned just stick with the promise api
The reason is as specified by #nikoss, you pass a future-returning function and cast it to a unit-returning function.
As for how to solve that, you can spawn the future on JS promise microtasks queue with spawn_local():
let (pc1_clone, dc1_clone) = (pc1.clone(), dc1.clone());
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| {
wasm_bindgen_futures::spawn_local(async move {
let desc = JsFuture::from(pc1_clone.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
});
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();

How can I accept invalid or self-signed SSL certificates in Rust futures reqwest?

My code looks like the following:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::get(&url).await {
// Ok and Err statements here!
}
But, the problem here is that it gives an error for URLs with invalid or self-signed SSL certificate. So, I tried to do the following:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::Client::builder().danger_accept_invalid_certs(true).build().unwrap().get(&url).await {
// Ok and Err statements here!
}
When I try to build it with Cargo, it says "error[E0277]: `RequestBuilder` is not a future".
So, how can I make my code accept invalid certificates?
Unlike the top-level get() function, which returns a Response, the Client::get() method which you call in the second snippet, returns a RequestBuilder, which you must send() to actually communicate.
Adding the missing send() allows the code to compile (playgropund):
fn main() {
let hosts: Vec<String> = vec![];
let fetches = futures::stream::iter(hosts.into_iter().map(|url| async move {
match reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap()
.get(&url)
.send()
.await
{
Ok(x) => x,
Err(x) => panic!(),
}
}));
}

How do I continue even when a function errors?

I'm using reqwest and scraper to write a simple web scraper in rust. One section of my code is given a link "next_page" that may or may not be valid. I want to try to access the link, and if there's an error, print the error and continue to the next link. Here's what I tried to do:
let next_page_response = reqwest::get(next_page).await;
let next_page_response = match next_page_response {
Ok(response) => response,
Err(error) => println!("WARN: Problem getting the url '{}'. \
The error was {:?}", next_page, error),
};
This code is wrapped in a loop, and next_page changes every iteration.
This doesn't work, rustc gives the error error[E0308]: 'match' arms have incompatible types. I suppose this makes sense, in the first arm the expression becomes a Response, whereas in the second arm it becomes a (). However, if I change println! to panic!, the code compiles.
Questions:
How can I acknowledge an error and then just continue?
Why does panic! work when println! doesn't?
Full code, for the curious.
As you allude to in the original post, the issue is that the return values of the two branches don't match. And as kmdreko mentions panic! works since it has a return type that can be used anywhere.
So if you want to avoid the panic you need to make the return values from each branch match.
One way is to have both arms return (). The code can be pretty simple if you put the processing of the successful response into the body of the match.
pub fn process_response(response: &Response) {
// ...
}
for next_page in pages {
let next_page_response = reqwest::get(next_page).await;
match next_page_response {
Ok(response) => process_response(response),
Err(error) => println!("WARN: Problem getting the url '{}'. \
The error was {:?}", next_page, error),
};
}
An alternate is to have both arms return an Option that you use later.
(In this case, it makes the code longer and uglier in my opinion, but there can be cases where it is useful). It could look something like this:
pub fn process_response(response: &Response) {
// ...
}
for next_page in pages {
let next_page_response = reqwest::get(next_page).await;
let next_page_response = match next_page_response {
Ok(response) => Some(response),
Err(error) => {
println!("WARN: Problem getting the url '{}'. \
The error was {:?}", next_page, error);
None
}
};
if let Some(response) = next_page_response {
process_response(response)
}
}

Resources