Rust: how to use await in function chain - rust

Desired code:
The commented out block compiles and works, however I'd like to move from nested matching style to a cleaner chain of functions
async fn ws_req_resp(msg: String, conn: PgConn) -> Result<String, Box<dyn std::error::Error>>{
let req: WSReq = serde_json::from_str(&msg)?;
match req.request_type{
"upsert_competitions" => {
// let dr = serde_json::from_value(req.data);
// match dr{
// Ok(d) => match upsert_competitions(conn, d).await{
// Ok(x) => serde_json::to_string(&x).map_err(|e| e.into()),
// Err(e) => Err(Box::new(e))
// }
// Err(e) => Err(Box::new(e))
// }
serde_json::from_value(req.data).and_then(|d| async move {
upsert_competitions(conn, d).await}).and_then(|r| serde_json::to_string(&r))
.map_err(|e| e.into())
},
uwotm8 => {
Err(Box::new(InvalidRequestError{req_type: uwotm8.to_string()}))
}
}
}
upsert_competitions signature pub async fn upsert_competitions(conn: PgConn, new: Vec<ApiNewCompetition>) -> Result<Vec<DbCompetition>, diesel::result::Error>
Error:
expected enum `std::result::Result<_, serde_json::error::Error>`
found opaque type `impl core::future::future::Future`
Have tried putting the await in multiple places in chain and none compile.
Believe awaiting a future should sit until it's finished, then return the result.
(It might be better for me to return a future from this function; and unwrap outside. However I do not understand why the await in the chain is failing, so clearly I'm lacking understanding...also trying to return a future I run into issues with compiler not knowing Size of return)
Full code
https://github.com/open-fantasy-sports/fantasy-sport-api-rust/blob/ef9db156efa8dbc159eae1c80fb7ac0a6a3ddee3/result_server/src/main.rs#L63

Someone told me can't use await in function chains like this.
When I switched to returning a Box<dyn std::error::Error + Sync + Send + 'static> error-type.
Then I was able to use ? operator successfully again for central code
(previously think it was failing due to ambiguous error return type).
So even without function changing could use this syntax to make code nice and readable.
updated code:
pub async fn upsert_competitions(req: WSReq, conn: PgConn, ws_conns: &mut WSConnections_, user_ws_id: Uuid) -> Result<String, BoxError>{
let deserialized: Vec<NewCompetition> = serde_json::from_value(req.data)?;
let competitions_out= db::upsert_competitions(&conn, deserialized.into_iter().map(transform_from).collect_vec())?;
if let Some(ws_user) = ws_conns.lock().await.get_mut(&user_ws_id){
sub_to_competitions(ws_user, competitions_out.iter().map(|c| &c.competition_id)).await;
}
publish_competitions(ws_conns, &competitions_out).await;
let resp_msg = WSMsgOut::resp(req.message_id, req.method, competitions_out);
serde_json::to_string(&resp_msg).map_err(|e| e.into())
}

Related

what is the correct way of implementing auth in actix_web?

I've been doing rust for just short while but didn't much struggle until this issue.
I want to authenticate each request, and make returned value (from auth service) available inside requests.
I've read implementing FromRequest for the struct returned by auth server should make it available.
But I have issues implementing it correctly. It requires extract and from_request functions both returning self, and if I understand it a little correctly, extract is being called the first, so I should set it somewhere in request extensions, and get it from there inside from_request. I have following code:
impl FromRequest for CustomClaim {
type Error = actix_web::Error;
type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
fn extract(req: &HttpRequest) -> Self::Future {
let auth_header = req
.headers()
.get("Authorization")
.unwrap()
.to_str()
.unwrap()
.to_owned();
let boxed_future = Box::pin(async move {
let claim_future = get_claim(&auth_header).await;
claim_future
});
req.extensions_mut().insert(boxed_future);
boxed_future
}
fn from_request(req: &HttpRequest, payload: &mut actix_web::dev::Payload) -> Self::Future {
let boxed_future = req.extensions().get::<Self::Future>().unwrap();
boxed_future
}
}
async fn get_claim(auth_header: &str) -> Result<CustomClaim, actix_web::Error>
But from_request doesn't compile because it returns a reference (error message: mismatched types expected struct `Pin\<Box\<(dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\> + 'static)\>\>`found reference`&Pin\<Box\<dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\>\>\>` )
Doing .clone() doesn't help because clone is not implemented on that future type so it still returns a reference. Can I implement clone for the future? Is my approach wrong?
Ok so I feel little dumb but the answer is - you (or I) don't need the extract function. Only from_request is required.
So solution is, move the code from extract function to from_request, omit extract, and don't set anything on the request:
impl FromRequest for CustomClaim {
type Error = actix_web::Error;
type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
fn from_request(req: &HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future {
let auth_header = req
.headers()
.get("Authorization")
.unwrap()
.to_str()
.unwrap()
.to_owned();
Box::pin(async move {
let claim_future = get_claim(&auth_header).await;
claim_future
})
}
}
Then it's possible to add a parameter in authenticated handlers:
#[post("/authed-post")]
async fn authed_post(
_req: HttpRequest,
claim: CustomClaim,
// ...
) -> impl Responder

Borrowed value does not live long enough when using async/await

I am trying to make two async api call and eventually get error E0597.
Here is a code:
async fn make_request() -> Result<()> {
.........
.........
.........
let mut result = client.get(uri).await?;
let some_key = result.headers().get("some_key");
let next_url = match some_key {
Some(url) => {
let some_result = client.get(Uri::from_static(url.to_str().unwrap())).await?
}
None => println!("....")
};
Ok(())
}
When I run this code the error "borrowed value does not live long enough argument requires that result is borrowed for `'static"
I have created a compile-able example based on your snipped to reproduce the error in the playground, and if you are able to do something like this in your question (for future reference), it usually helps you get more specific answers.
The Request passed into the function has no lifetime guarantees, so this will fail with the error you mentioned:
use http::{Request, Uri};
async fn make_request(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// `url` is a reference to the string in the "some_key" header
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
You can add that lifetime requirement, but that probably isn't what you need, and will likely give you the same error message, just in a different place:
async fn make_request_static(result: &'static Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
Uri implements the FromStr trait, though, so you would be best off using that. There is no longer a lifetime requirement, so it can work with any string you pass in, even one which is currently borrowed:
// need to import the trait to use its methods
use std::str::FromStr;
async fn make_request_3(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_str(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}

StreamExt::forward fails with error `expected integer, found enum `std::result::Result``

I'm trying to connect a rust futures stream (repeat) to a sink (drain).
The first method works fine (check below), but the second one with .forward fails to compile (the error is shown inline). Any pointers on what I'm doing wrong?
use futures::stream::StreamExt;
use futures::sink::SinkExt;
use futures::executor;
fn main() {
executor::block_on( async {
// works
let mut work = futures::stream::repeat(5);
let mut drain = futures::sink::drain();
match drain.send(work.next()).await {
Ok(_r) => println!("Done"),
Err(_e) => println!("Error"),
};
// works
let res: Vec<i32> = futures::stream::repeat(5).take(3).collect::<Vec<i32>>().await;
println!("{:?}", res);
// doesn't work
// futures::stream::repeat(5).forward(futures::sink::drain());
// ^^^^^^^ expected integer, found enum `std::result::Result`
// error[E0271]: type mismatch resolving `<futures_util::stream::repeat::Repeat<{integer}> as futures_core::stream::Stream>::Item == std::result::Result<_, _>`
// Fixed above with repeat(Ok(5)) but not repeat(5)
match futures::stream::repeat(Ok(5)).take(3).forward(futures::sink::drain()).await {
Ok(_r) => println!("Done"),
Err(_e) => println!("Error"),
};
});
}
Relevant docs
https://docs.rs/futures/0.3.1/futures/stream/fn.repeat.html
https://docs.rs/futures/0.3.1/futures/sink/fn.drain.html
https://docs.rs/futures/0.3.1/futures/stream/trait.StreamExt.html#method.forward
I guess you wanted something a bit more like this?
use futures::stream::StreamExt;
use futures::sink::SinkExt;
#[tokio::main]
async fn main() {
// works
let mut work = futures::stream::repeat(5);
let mut drain = futures::sink::drain();
match drain.send(work.next()).await {
Ok(_r) => println!("Done"),
Err(_e) => println!("Error"),
};
// doesn't work
futures::stream::repeat(Ok(5)).forward(futures::sink::drain());
}
As the error (slightly confusingly) illustrates, the repeat call must take a Result argument in this case.

How to return a Result containing a serde_json::Value?

This is what I have, but I want to avoid using unwrap on my reqwest values:
extern crate base64;
extern crate reqwest;
use serde_json;
use serde_json::json;
pub fn perform_get(id: String) -> serde_json::value::Value {
let client = reqwest::Client::builder().build().unwrap();
let url = String::from("SomeURL");
let res = client.get(&url).send().unwrap().text();
let mut v = json!(null);
match res {
Ok(n) => {
v = serde_json::from_str(&n).unwrap();
}
Err(r) => {
println!("Something wrong happened {:?}", r);
}
}
v
}
fn main() {
println!("Hi there! i want the function above to return a result instead of a Serde value so I can handle the error in main!");
}
Here is a link to a rust playground example
The official Rust book, The Rust Programming Language, is freely available online. It has an entire chapter on using Result, explaining introductory topics such as the Result enum and how to use it.
How to return a Result containing a serde_json::Value?
The same way you return a Result of any type; there's nothing special about Value:
use serde_json::json; // 1.0.38
pub fn ok_example() -> Result<serde_json::value::Value, i32> {
Ok(json! { "success" })
}
pub fn err_example() -> Result<serde_json::value::Value, i32> {
Err(42)
}
If you have a function that returns a Result, you can use the question mark operator (?) to exit early from a function on error, returning the error. This is a concise way to avoid unwrap or expect:
fn use_them() -> Result<(), i32> {
let ok = ok_example()?;
println!("{:?}", ok);
let err = err_example()?;
println!("{:?}", err); // Never executed, we always exit due to the `?`
Ok(()) // Never executed
}
This is just a basic example.
Applied to your MCVE, it would look something like:
use reqwest; // 0.9.10
use serde_json::Value; // 1.0.38
type Error = Box<dyn std::error::Error>;
pub fn perform_get(_id: String) -> Result<Value, Error> {
let client = reqwest::Client::builder().build()?;
let url = String::from("SomeURL");
let res = client.get(&url).send()?.text()?;
let v = serde_json::from_str(&res)?;
Ok(v)
}
Here, I'm using the trait object Box<dyn std::error::Error> to handle any kind of error (great for quick programs and examples). I then sprinkle ? on every method that could fail (i.e. returns a Result) and end the function with an explicit Ok for the final value.
Note that the panic and the never-used null value can be removed with this style.
See also:
What is this question mark operator about?
Rust proper error handling (auto convert from one error type to another with question mark)
Rust return result error from fn
Return value from match to Err(e)
What is the idiomatic way to handle/unwrap nested Result types?
better practice to return a Result
See also:
Should I avoid unwrap in production application?
If you are in the user side I would suggest to use Box<dyn std::error::Error>, this allow to return every type that implement Error, ? will convert the concrete error type to the dynamic boxed trait, this add a little overhead when there is an error but when error are not expected or really rare this is not a big deal.
use reqwest;
use serde_json::value::Value;
use std::error::Error;
fn perform_get(_id: String) -> Result<Value, Box<dyn Error>> {
let client = reqwest::Client::builder().build()?;
let url = String::from("SomeURL");
let res = client.get(&url).send()?.text()?;
let v = serde_json::from_str(&res)?;
Ok(v)
// last two line could be serde_json::from_str(&res).map_err(std::convert::Into::into)
}
fn main() {
println!("{:?}", perform_get("hello".to_string()));
}
This produce the following error:
Err(Error { kind: Url(RelativeUrlWithoutBase), url: None })
The kind smart folks over at Rust Discord helped me solve this one. (user noc)
extern crate base64;
extern crate reqwest;
pub fn get_jira_ticket() -> Result<serde_json::value::Value, reqwest::Error> {
let client = reqwest::Client::builder().build().unwrap();
let url = String::from("SomeURL");
let res = client.get(&url).send().and_then(|mut r| r.json());
res
}
fn main() {
println!("This works");
}
The key part was this in the header for the return
-> Result<serde_json::value::Value, reqwest::Error>
And this here to actually return the data.
client.get(&url).send().and_then(|mut r| r.json());

How can I return something meaningful from a generic function if there is nothing to return?

I'm building a library in Rust that has a send method that performs HTTP requests against a local RPC server using reqwest.
This method returns a generic type R in a Result where R: DeserializeOwned. After making the correct types for every response, serde_json::from_str() can get me the type.
If there is no response upon a request, how can I make send still return something meaningful?
This is the code I have now:
fn send<R, T>(
&self,
request: &RpcRequest<T>,
) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
let res = serde_json::from_str(&buf).map_err(|err| ClientError::Json(err))
I am now forced to create and return an Err, but technically, the request returning no response is expected behavior, so I want to return something other than an Err.
I tried to work around this by wrapping R with Option, but that means I have to double unwrap every response, and 98% of the responses from reqwest do have data in their response, so it feels a bit like overkill.
I also tried to return a self-made EmptyResponse type, but the compiler complains: expected type R, found type EmptyResponse. I think returning a type EmptyResponse would be what I want, but maybe someone can shed some tips on how to maybe do this even better.
You can return an Result<Option<R>, ApiError> as shown in the documentation, then match it like this:
match sender.send(request) {
Ok(Some(r)) => {
// process response
}
Ok(None) => {
// process empty response
}
Err(e) => {
// process error
}
}
// or
if let Ok(Some(r)) = sender.send(request) {
// process response
}
I tried to work around this by wrapping R with Option, but that means I have to double unwrap every response, and 98% of the responses from reqwest do have data in their response, so it feels a bit like overkill.
Unwrapping the Option is a very cheap operation, there's nothing to be worried about.
The pragmatic answer is to have two functions:
fn send<R, T>(&self, request: &RpcRequest<T>) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
fn send_no_response<T>(&self, request: &RpcRequest<T>) -> Result<(), ApiError>
where
T: Serialize + Debug,
If your server happens to return a value that can be deserialized into the type (), then you can avoid the overhead of two functions. However, this is not the case for JSON, one of the most common formats:
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
fn send<R>() -> Result<R, Error>
where
R: DeserializeOwned,
{
serde_json::from_str("").map_err(Into::into)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}
This panics:
Unable to deserialize: Error("EOF while parsing a value", line: 1, column: 0)
In a world with specialization, you can use it and a helper trait to reduce back to one function:
#![feature(specialization)]
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
type ApiResponse = &'static str;
trait FromApi: Sized {
fn convert(response: ApiResponse) -> Result<Self, Error>;
}
impl<R> FromApi for R
where
R: DeserializeOwned,
{
default fn convert(response: ApiResponse) -> Result<R, Error> {
eprintln!("deserializing the response");
serde_json::from_str(response).map_err(Into::into)
}
}
impl FromApi for () {
fn convert(_response: ApiResponse) -> Result<Self, Error> {
eprintln!("Ignoring the response");
Ok(())
}
}
fn send<R: FromApi>() -> Result<R> {
eprintln!(r#""sending" the request"#);
let api_response = "";
R::convert(api_response)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}

Resources