How can I get JSON from the Github API? - rest

I just want to get a JSON from the following URL.
So I used this code:
extern crate reqwest;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let res = reqwest::Client::new()
.get("https://api.github.com/users/octocat")
.send()?
.text()?;
println!("{}", res);
Ok(())
}
But I don't know how to solve the error :
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
--> src\main.rs:19:15
|
19 | let res = reqwest::Client::new()
| _______________^
20 | | .get("https://api.github.com/users/octocat")
21 | | .send()?
| |________________^ the `?` operator cannot be applied to type `impl std::future::Future`
|
= help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
= note: required by `std::ops::Try::into_result`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `Desktop`.
but I can also obtain what I want with a simple
curl https://api.github.com/users/octocat
I've tried to add use std::ops::Try; but it doesn't work better.

The reqwest crate uprovides an asynchronous api by default. Therefore you have to .await before handling the error with the ? operator. You also have to use an async runtime, such as tokio:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::Client::new()
.get("https://api.github.com/users/octocat")
.send()
.await?
.json::<std::collections::HashMap<String, String>>()
.await?;
println!("{:#?}", resp);
Ok(())
}
Note that to use convert the response to json as shown above, you must enable the json feature in your Cargo.toml:
reqwest = { version = "0.10.8", features = ["json"] }
If you don't want to use an async runtime, you can enable the blocking reqwest client:
[dependencies]
reqwest = { version = "0.10", features = ["blocking", "json"] }
And use it like so:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::blocking::Client::new()
.get("https://api.github.com/users/octocat")
.send()?
.json::<std::collections::HashMap<String, String>>()?;
println!("{:#?}", resp);
Ok(())
}
Github's api requires a couple other config options. Here is a minimal working example with reqwest and the github api:
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use serde::{Deserialize};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut headers = HeaderMap::new();
// add the user-agent header required by github
headers.insert(USER_AGENT, HeaderValue::from_static("reqwest"));
let resp = reqwest::blocking::Client::new()
.get("https://api.github.com/users/octocat")
.headers(headers)
.send()?
.json::<GithubUser>()?;
println!("{:#?}", resp);
Ok(())
}
// Note that there are many other fields
// that are not included for this example
#[derive(Deserialize, Debug)]
pub struct GithubUser {
login: String,
id: usize,
url: String,
#[serde(rename = "type")]
ty: String,
name: String,
followers: usize
}

Related

'Bson: Borrow<T> is not satisifed' error in simple CRUD post function

I have following Error:
error[E0277]: the trait bound `Bson: Borrow<News>` is not satisfied
--> src\handlers.rs:46:36
|
46 | let inserted = coll.insert_one(serialized_news, None).await.unwrap();
| ---------- ^^^^^^^^^^^^^^^ the trait `Borrow<News>` is not implemented for `Bson`
| |
| required by a bound introduced by this call
|
note: required by a bound in `mongodb::Collection::<T>::insert_one`
--> C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\mongodb-2.3.1\src\coll\mod.rs:1279:19
|
1279 | doc: impl Borrow<T>,
| ^^^^^^^^^ required by this bound in `mongodb::Collection::<T>::insert_one`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `backend` due to previous error
My handler code containing the post_news function that throws this error:
use crate::structs::News;
use axum::{extract::Path, extract::State, http::StatusCode, response::IntoResponse, Json};
use bson::oid::ObjectId;
use futures::stream::StreamExt;
use mongodb::{bson::doc, options::FindOptions, Client, Collection};
pub async fn get_all(State(client): State<Client>) -> impl IntoResponse {
let coll: Collection<News> = client.database("axum").collection::<News>("news");
let mut options = FindOptions::default();
options.limit = Some(1);
options.sort = Some(doc! {
"title": 1
});
let mut cursor = coll
.find(None, options)
.await
.expect("could not load news data.");
let mut rows: Vec<News> = Vec::new();
while let Some(doc) = cursor.next().await {
rows.push(doc.expect("could not load news info."));
}
(StatusCode::OK, Json(rows))
}
pub async fn get_one(Path(id): Path<u64>) {}
pub async fn post_news(
State(client): State<Client>,
Json(payload): Json<News>,
) -> impl IntoResponse {
let coll: Collection<News> = client.database("axum").collection::<News>("news");
let news = News {
id: ObjectId::new(),
title: payload.title.to_string(),
short_description: payload.short_description.to_string(),
};
let serialized_news = bson::to_bson(&news).unwrap();
let inserted = coll.insert_one(serialized_news, None).await.unwrap();
(StatusCode::CREATED, Json(news))
}
pub async fn handler_404() -> impl IntoResponse {
(StatusCode::NOT_FOUND, "nothing to see here")
}
mod structs {
use bson::{self, oid::ObjectId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct News {
#[serde(rename = "_id")]
pub id: ObjectId,
pub title: String,
pub short_description: String,
}
}
The error occurs in this line in the post_news function:
let inserted = coll.insert_one(serialized_news, None).await.unwrap();
Tried to add Clone and Debug to my struct and that didn't help
When encountering an error, you need to dig in and figure out why the error occurs, rather than just fiddling left and right.
In this case, the first step is looking at the function you are attempting to call Collection::insert_one:
pub async fn insert_one(
&self,
doc: impl Borrow<T>,
options: impl Into<Option<InsertOneOptions>>
) -> Result<InsertOneResult>
And double checking what Borrow is by following the link -- just to make sure it's indeed std::borrow::Borrow (spoiler: it is).
So, since your coll is of type Collection<News>, you need a type implementing Borrow<News> -- as mentioned by the error message -- which means amongst others News and &News.
Hence, as shown in the documentation of Collection, you should just insert your News object, without first serializing it:
let inserted = coll.insert_one(news, None).await.unwrap();
Collection will take care of serializing and deserializing as appropriate, no need to do the bson step yourself.

Rust compiler error: future cannot be sent between threads safely required for the cast from X to Y

I have seen other questions about similar errors with implementing send, but my error message is different. This is the error message:
future cannot be sent between threads safely
required for the cast from `impl warp::Future<Output = Result<std::string::String, std::string::String>>` to the object type `dyn warp::Future<Output = Result<std::string::String, std::string::String>> + std::marker::Send`
I'm getting this on a function on an implementation of an async_trait. Here's the impl:
use std::borrow::Borrow;
use async_trait::async_trait;
use mongodb::{
bson::{doc, oid::ObjectId, Document},
error::Error,
options::FindOneOptions,
results::InsertOneResult,
Client,
};
use serde::{de::DeserializeOwned, Serialize};
pub struct MessagesRepository<'a> {
pub(crate) client: &'a Client,
}
#[async_trait]
impl<'a> super::Repository<'a> for MessagesRepository<'a> {
fn with_client(client: &'a Client) -> Self {
MessagesRepository { client: client }
}
async fn find_one<'b, T: DeserializeOwned + Sync + Send + Unpin>(
&self,
filter: Document,
find_one_options: FindOneOptions,
) -> Result<Option<T>, Error> {
let collection = self
.client
.database("App")
.collection::<T>("Messages");
collection.find_one(filter, find_one_options).await
}
async fn insert_one<'b, T: DeserializeOwned + Sync + Send + Unpin + Serialize>(
&self,
new_document: impl Borrow<T>,
) -> Result<String, String> {
let collection = self
.client
.database("App")
.collection::<T>("Messages");
let result = collection.insert_one(new_document, None).await;
match result {
Ok(_) => Ok("Success".to_string()),
Err(_) => Err("Error".to_string()),
}
}
}
This is the trait:
use std::borrow::Borrow;
use async_trait::async_trait;
use mongodb::{
bson::Document, error::Error, options::FindOneOptions, results::InsertOneResult, Client,
};
use serde::{de::DeserializeOwned, Serialize};
#[async_trait]
pub trait Repository<'a> {
fn with_client(client: &'a Client) -> Self;
async fn find_one<'b, T: DeserializeOwned + Sync + Send + Unpin>(
&self,
filter: Document,
find_one_options: FindOneOptions,
) -> Result<Option<T>, Error>;
async fn insert_one<'b, T: DeserializeOwned + Sync + Send + Unpin + Serialize>(
&self,
new_document: impl Borrow<T>,
) -> Result<String, String>;
}
Anyone have any ideas? I'm using the mongodb rust driver. Thanks!

Learning Rust, having trouble with async_traits and borrowing. `xxxxx` dropped here while still borrowed

I've viewed so many stack overflows, but can't get my head around how to fix this.
Here are my deps:
[dependencies]
mongodb = { version = "2.3.1"}
tokio = "1.22.0"
twilio-openapi = "1.0.0"
async-trait = "0.1.58"
futures = "0.3.25"
I've created this async_trait repository trait for implementing various data access repositories:
use std::pin::Pin;
use async_trait::async_trait;
use futures::Future;
use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client};
pub mod notification_settings;
#[async_trait]
pub trait Repository<'a> {
fn with_client(client: &'a mut &Client) -> Self;
async fn find_one<'b>(
self,
filter: Document,
find_one_options: FindOneOptions,
) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>>;
}
It seems like async_trait sort of requires returning a Pin<Box<dyn Future<Output = X> + Send + 'b)>> Where you can specify a certain lifetime on the borrow. I'm not sure exactly why or if there are ways around this, but again still learning this.
And then here is my struct implementing the trait:
use std::{marker::Send, pin::Pin};
use async_trait::async_trait;
use futures::Future;
use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client};
pub struct NotificationSettingsRepository<'a> {
pub(crate) client: &'a Client,
}
#[async_trait]
impl<'a> super::Repository<'a> for NotificationSettingsRepository<'a> {
fn with_client(client: &'a mut &Client) -> Self {
NotificationSettingsRepository { client: client }
}
async fn find_one<'b>(
self,
filter: Document,
find_one_options: FindOneOptions,
) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>> {
let collection = self
.client
.database("Occasionally")
.collection::<Document>("NotificationSettings");
let document = collection.find_one(filter, find_one_options);
Box::pin(async { document.await })
}
}
Problem is on collection.find_one(filter, find_one_options); I get an error about:
`collection` does not live long enough
borrowed value does not live long enoughrustcClick for full compiler diagnostic
notification_settings.rs(30, 5): `collection` dropped here while still borrowed
notification_settings.rs(17, 23): lifetime `'b` defined here
notification_settings.rs(21, 10): type annotation requires that `collection` is borrowed for `'b`
This makes some sense because the definition of find_one looks like this:
find_one(&self, filter: impl Into<Option<Document>>, options: impl Into<Option<FindOneOptions>>) -> Result<Option<T>>
So it is borrowing the collection via &self
I've tried a few things like adding an async { } wrapper on the Future of find_one. But I'm not sure what I'm doing there. Please help and thank you!

Succesfully return a connection instance MongoDb - Rust

I'm learning rust, and I have been following the rust book. Usually, I try to implement something to strengthen my knowledge.
Recently I was looking into how to use the MongoDb create to create an instace and return the name of the database.
Here's a sample of what I have:
A folder containing a implementation to build a mongo connection string.
A .env file
A main function to get the info I need.
mongo_connection.rs
#[derive(Debug)]
pub struct ConnectionString {
pub username: String,
pub password: String,
pub cluster: String,
}
impl ConnectionString {
pub fn build_connection_string() -> String {
return format!("mongodb+srv://{}:{}#a{}.k1jklnx.mongodb.net/?retryWrites=true&w=majority",
Self.username, Self.password, Self.cluster)
}
}
main.rs
mod database;
use crate::database::mongo_connection;
use mongodb::{Client, options::ClientOptions};
use std::error::Error;
use dotenv::dotenv;
use std::env;
use tokio;
async fn create_database_connection() -> Client {
dotenv().ok(); //Loading environment variables from .env file
let connection_parameters = mongo_connection::ConnectionString{
username: env::var("USERNAME").expect("No username found on .env"),
password: env::var("PASSWORD").expect("No password found on .env"),
cluster: env::var("CLUSTER").expect("No cluster found on .env")
};
let mut url: String = mongo_connection::ConnectionString::build_connection_string();
println!("{}", url);
let options = ClientOptions::parse(&url).await?;
return Client::with_options(options).await;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = create_database_connection().await?;
let db = client.database(&"runt");
println!("{:?}", db.name());
Ok(())
}
The problem I am facing is that I need to return a connection client. But I keep getting the Error:
let options = ClientOptions::parse(&url).await?;
^ cannot use the `?` operator in an async function that returns `Client`
If I change the result of the function to something like: Result<Client, dyn Eq> then I get the error:
async fn create_database_connection() -> Result<Client, dyn Eq> {
`Eq` cannot be made into an object
^^^^^^^^^^^^^^^ the trait cannot be made into an object because it uses `Self` as a type parameter
|
Any advice on how to solve the error or any workaround is much appreciated.
You could change the return type to either Result<Client, mongodb::error::Error> or Result<Client, Box<dyn Error>>. Either should work, but here's the code with the first version:
async fn create_database_connection() -> Result<Client, mongodb::error::Error> {
dotenv().ok(); //Loading environment variables from .env file
let connection_parameters = mongo_connection::ConnectionString{
username: env::var("USERNAME").expect("No username found on .env"),
password: env::var("PASSWORD").expect("No password found on .env"),
cluster: env::var("CLUSTER").expect("No cluster found on .env")
};
let mut url: String = mongo_connection::ConnectionString::build_connection_string();
println!("{}", url);
let options = ClientOptions::parse(&url).await?;
return Client::with_options(options).await;
}
In case you want to use the Box<dyn Error> version, the return value also needs a .into() to convert the Result<Client, mongodb::error::Error> of Client::with_options() into a Result<Client, Box<dyn Error>>.

How can I serve static files / a directory in hyper?

I want to serve some static files (.js, .css, ...) from my hyper server.
Currently the only way I can think of is inlining the files as strings / load them on startup.
Is there a better way to directly serve an entire directory or selected files?
After typing in the words "hyper static" into crates.io, the first result was hyper-staticfile. The project's GitHub repository has an examples directory, with one such example:
extern crate futures;
extern crate hyper;
extern crate hyper_staticfile;
extern crate tokio_core;
// This example serves the docs from target/doc/hyper_staticfile at /doc/
//
// Run `cargo doc && cargo run --example doc_server`, then
// point your browser to http://localhost:3000/
use futures::{Future, Stream, future};
use hyper::Error;
use hyper::server::{Http, Request, Response, Service};
use hyper_staticfile::Static;
use std::path::Path;
use tokio_core::reactor::{Core, Handle};
use tokio_core::net::TcpListener;
type ResponseFuture = Box<Future<Item=Response, Error=Error>>;
struct MainService {
static_: Static,
}
impl MainService {
fn new(handle: &Handle) -> MainService {
MainService {
static_: Static::new(handle, Path::new("target/doc/")),
}
}
}
impl Service for MainService {
type Request = Request;
type Response = Response;
type Error = Error;
type Future = ResponseFuture;
fn call(&self, req: Request) -> Self::Future {
if req.path() == "/" {
let res = Response::new()
.with_status(hyper::StatusCode::MovedPermanently)
.with_header(hyper::header::Location::new("/hyper_staticfile/"));
Box::new(future::ok(res))
} else {
self.static_.call(req)
}
}
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr = "127.0.0.1:3000".parse().unwrap();
let listener = TcpListener::bind(&addr, &handle).unwrap();
let http = Http::new();
let server = listener.incoming().for_each(|(sock, addr)| {
let s = MainService::new(&handle);
http.bind_connection(&handle, sock, addr, s);
Ok(())
});
println!("Doc server running on http://localhost:3000/");
core.run(server).unwrap();
}