How to conditionally add routes to Axum Router? - rust

I'm using axum and this code to create a server but I get an error:
use axum::{response::Html, routing::get, Router};
async fn handler() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}
#[tokio::main]
async fn main() {
let router = Router::new();
router.route("/", get(handler));
if true { // I need to check something here
router.route("/other", get(handler));
}
axum::Server::bind(&([127, 0, 0, 1], 3000).into())
.serve(router.into_make_service())
.await
.unwrap();
}
error[E0382]: use of moved value: `router`
--> src\main.rs:18:16
|
9 | let router = Router::new();
| ------ move occurs because `router` has type `Router`, which does not implement the `Copy` trait
10 |
11 | router.route("/", get(handler));
| ------ value moved here
...
14 | router.route("/other", get(handler));
| ------ value moved here
...
18 | .serve(router.into_make_service())
| ^^^^^^ value used here after move
I tried with &router but I still get the error. How can I fix this? Should I use .clone()?

Using this code it works:
let mut router = Router::new();
router = router.route("/", get(handler));
if true {
router = router.route(...);
}

Here is simplified code (Axum does the same thing). route takes ownership but also returns it.
#[derive(Debug)]
struct Router {
list: Vec<String>,
}
impl Router {
fn new() -> Self {
Router { list: vec![] }
}
fn route(mut self, path: String) -> Self {
self.list.push(path);
self
}
}
fn main() {
let mut router = Router::new().route("/1".to_owned()).route("/2".to_owned());
if true {
router = router.route("/3".to_owned());
}
println!("{:?}", router);
}
So you have to set router in-line, or make it mut and reassign result of route to router variable.

Related

How to use a struct method as an handler in Axum? [duplicate]

This question already has answers here:
How to call struct method from axum server route?
(1 answer)
Is there a way to create a function pointer to a method in Rust?
(1 answer)
Closed 2 months ago.
This works where the handler is a stand alone function
use axum::{
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,
};
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> () {
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", axum::routing::get(root));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// basic handler that responds with a static string
async fn root() -> impl IntoResponse {
let person = Person {
age: 20,
name: "John".to_string()
};
(StatusCode::OK, Json(person))
}
but in my project, I need to have the logic of the handler as part of a struct impl. I tried doing that but it fails. Code and error is below
use axum::{
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,
};
use std::net::SocketAddr;
mod http {
pub struct ServerLogic {
}
impl ServerLogic {
pub fn new() -> Self {
Self {}
}
pub fn hello(&self) -> impl axum::response::IntoResponse {
(axum::http::StatusCode::OK, axum::Json("Hello, world!"))
}
}
}
#[tokio::main]
async fn main() -> () {
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", axum::routing::get(crate::http::ServerLogic::new().hello));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
Error
error[E0615]: attempted to take value of method `hello` on type `ServerLogic`
--> src/main.rs:104:72
|
104 | .route("/", axum::routing::get(crate::http::ServerLogic::new().hello));
| ^^^^^ method, not a field
|
help: use parentheses to call the method
|
104 | .route("/", axum::routing::get(crate::http::ServerLogic::new().hello()));
| ++
How do I get to use the method of a struct as an handler?

Error in Async closure: Lifetime may not live long enough; returning this value requires that `'1` must outlive `'2`

I'm trying to use a Mutex<Sender> inside an async closure but I'm not entirely sure why I'm getting the error below:
error: lifetime may not live long enough
--> src/main.rs:120:41
|
120 | io.add_method(MARK_ITSELF, move |_| async {
| ________________________________--------_^
| | | |
| | | return type of closure `impl std::future::Future<Output = Result<jsonrpc::serde_json::Value, jsonrpc_http_server::jsonrpc_core::Error>>` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
121 | | trace!("Mark itself!");
122 | | let tx = sender.lock().map_err(to_internal)?;
123 | | tx.send(Action::MarkItself)
124 | | .map_err(to_internal)
125 | | .map(|_| Value::Bool(true))
126 | | });
| |_____^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
My main function looks like this:
use jsonrpc::serde_json::Value;
use jsonrpc::Error as ClientError;
use jsonrpc::{
serde_json::value::RawValue,
simple_http::{self, SimpleHttpTransport},
Client,
};
use jsonrpc_http_server::jsonrpc_core::{Error as ServerError, IoHandler};
use jsonrpc_http_server::ServerBuilder;
use log::{debug, error, trace};
use serde::Deserialize;
use std::rc::Rc;
use std::sync::mpsc::Receiver;
use std::sync::Mutex;
use std::thread;
use std::{
env, fmt,
net::SocketAddr,
sync::mpsc::{channel, Sender},
};
const START_ROLL_CALL: &str = "start_roll_call";
const MARK_ITSELF: &str = "mark_itself";
fn create_client(url: &str, user: &str, pass: &str) -> Result<Client, simple_http::Error> {
let t = SimpleHttpTransport::builder()
.url(url)?
.auth(user, Some(pass))
.build();
Ok(Client::with_transport(t))
}
fn spawn_worker() -> Result<Sender<Action>, failure::Error> {
let (tx, rx): (Sender<Action>, Receiver<Action>) = channel();
let next: SocketAddr = env::var("NEXT")?.parse()?;
thread::spawn(move || {
let remote = Remote::new(next).unwrap();
let mut in_roll_call = false;
for action in rx.iter() {
match action {
Action::StartRollCall => {
if !in_roll_call {
if remote.start_roll_call().is_ok() {
debug!("ON");
in_roll_call = true;
}
} else {
if remote.mark_itself().is_ok() {
debug!("OFF");
in_roll_call = false;
}
}
}
Action::MarkItself => {
if in_roll_call {
if remote.mark_itself().is_ok() {
debug!("OFF");
in_roll_call = false;
}
} else {
debug!("SKIP");
}
}
}
}
});
Ok(tx)
}
enum Action {
StartRollCall,
MarkItself,
}
struct Remote {
client: Client,
}
impl Remote {
fn new(addr: SocketAddr) -> Result<Self, simple_http::Error> {
let url = format!("http://{}", addr);
let client = create_client(&url, "", "")?;
Ok(Self { client })
}
fn call_method<T>(&self, method: &str, params: &[Box<RawValue>]) -> Result<T, ClientError>
where
T: for<'de> Deserialize<'de>,
{
let request = self.client.build_request(method, params);
self.client
.send_request(request)
.and_then(|res| res.result::<T>())
}
fn start_roll_call(&self) -> Result<bool, ClientError> {
self.call_method(START_ROLL_CALL, &[])
}
fn mark_itself(&self) -> Result<bool, ClientError> {
self.call_method(MARK_ITSELF, &[])
}
}
fn main() -> Result<(), failure::Error> {
env_logger::init();
let tx = spawn_worker()?;
let addr: SocketAddr = env::var("ADDRESS")?.parse()?;
let mut io = IoHandler::default();
let sender = Mutex::new(tx.clone());
io.add_method(START_ROLL_CALL, move |_| async move {
trace!("Starting roll call!");
let tx = sender.lock().map_err(to_internal)?;
tx.send(Action::StartRollCall)
.map_err(to_internal)
.map(|_| Value::Bool(true))
});
let sender = Mutex::new(tx.clone());
io.add_method(MARK_ITSELF, move |_| async {
trace!("Mark itself!");
let tx = sender.lock().map_err(to_internal)?;
tx.send(Action::MarkItself)
.map_err(to_internal)
.map(|_| Value::Bool(true))
});
let server = ServerBuilder::new(io).start_http(&addr)?;
Ok(server.wait())
}
fn to_internal<E: fmt::Display>(err: E) -> ServerError {
error!("Error: {}", err);
ServerError::internal_error()
}
The main idea is to pass the mspc sender to the closure so that the method can send the Action(An enum). Is there something I'm doing wrong?
The problem is add_method requires that
your closure is static, and
the Future returned from your closure is static.
Try this
let sender = Arc::new(Mutex::new(tx.clone()));
io.add_method(START_ROLL_CALL, move |_| {
let cloned_sender = sender.clone();
async move {
trace!("Starting roll call!");
let tx = cloned_sender.lock().map_err(to_internal)?;
tx.send(Action::StartRollCall)
.map_err(to_internal)
.map(|_| Value::Bool(true))
}
});
Side note: you are using sync Mutex in an async environment. This undermines the benefits of async. Consider using async Mutex such as Tokio Mutex or async-lock Mutex.

Warp: single route works, multiple with .or() do not

Hoping someone can help me understand why running warp with a single route like this compiles fine:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// GET /stats
let stats = warp::get()
.and(warp::path("stats"))
.map(|| {
let mut sys = System::new_all();
sys.refresh_all();
let local = LocalSystem::from_sys(&sys);
warp::reply::json(&local);
});
// GET /
let index = warp::get()
.and(warp::path::end())
.map(|| warp::reply::json(&last_ten_logs()));
warp::serve(index).run(([127, 0, 0, 1], 4000)).await;
Ok(())
}
But changing the warp::serve() line to serve two routes like in the examples in the repo causes a compilation error:
error[E0277]: the trait bound `(): Reply` is not satisfied
--> src/main.rs:140:17
|
140 | warp::serve(stats.or(index)).run(([127, 0, 0, 1], 4000)).await;
| ----------- ^^^^^^^^^^^^^^^ the trait `Reply` is not implemented for `()`
| |
| required by a bound introduced by this call
|
= note: required because of the requirements on the impl of `Reply` for `((),)`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `Reply` for `(warp::generic::Either<((),), (Json,)>,)`
I don't understand what the compiler is asking me to change.
The error is explicit:
the trait Reply is not implemented for ()
The problem is that your stats endpoint do not return anything, just remove the last ; so it gets returned as the last expresion in the closure:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// GET /stats
let stats = warp::get()
.and(warp::path("stats"))
.map(|| {
let mut sys = System::new_all();
sys.refresh_all();
let local = LocalSystem::from_sys(&sys);
warp::reply::json(&local)
});
...
}

Rust "this parameter and the return type are declared with different lifetimes"

I'm using the smol library from Rust. None of the other answers to this question helped.
The smol's Executor::spawn() is declared like so:
pub fn spawn<T: Send + 'a>(&self, future: impl Future<Output = T> + Send + 'a) -> Task<T> {
Now I have a function and want to call spawn recursively like so:
async fn start(executor: &Executor<'_>) {
let server_task = executor.spawn(async {
executor.spawn(async { println!("hello"); }).await;
});
}
But I'm getting this error:
9 | async fn start(executor: &Executor<'_>) {
| ------------ -
| |
| this parameter and the return type are declared with different lifetimes...
...
18 | let server_task = executor.spawn(async {
| ^^^^^ ...but data from `executor` is returned here
How can I resolve this error? I'm very confused.
use {
smol::{block_on, Executor},
std::sync::Arc,
};
// --
fn main() {
let ex = Arc::new(Executor::new());
block_on(ex.run(start(ex.clone())));
}
async fn start(executor: Arc<Executor<'_>>) {
let ex2 = executor.clone();
let server_task = executor.spawn(async move {
let t = ex2.spawn(async {
println!("hello");
});
t.await;
});
server_task.await;
}

Can't get async closure to work with Warp::Filter

I am trying to get an async closure working in the and_then filter from Warp.
This is the smallest example I could come up with where I am reasonably sure I didn't leave any important details out:
use std::{convert::Infallible, sync::Arc, thread, time};
use tokio::sync::RwLock;
use warp::Filter;
fn main() {
let man = Manifest::new();
let check = warp::path("updates").and_then(|| async move { GetAvailableBinaries(&man).await });
}
async fn GetAvailableBinaries(man: &Manifest) -> Result<impl warp::Reply, Infallible> {
Ok(warp::reply::json(&man.GetAvailableBinaries().await))
}
pub struct Manifest {
binaries: Arc<RwLock<Vec<i32>>>,
}
impl Manifest {
pub fn new() -> Manifest {
let bins = Arc::new(RwLock::new(Vec::new()));
thread::spawn(move || async move {
loop {
thread::sleep(time::Duration::from_millis(10000));
}
});
Manifest { binaries: bins }
}
pub async fn GetAvailableBinaries(&self) -> Vec<i32> {
self.binaries.read().await.to_vec()
}
}
I am using:
[dependencies]
tokio = { version = "0.2", features = ["full"] }
warp = { version = "0.2", features = ["tls"] }
The error is:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/main.rs:9:48
|
9 | let check = warp::path("updates").and_then(|| async move { GetAvailableBinaries(&man).await });
| -------- ^^^^^^^^^^^^^ ------------------------------------ closure is `FnOnce` because it moves the variable `man` out of its environment
| | |
| | this closure implements `FnOnce`, not `Fn`
| the requirement to implement `Fn` derives from here
After making Manifest implement Clone, you can fix the error by balancing when the manifest object is cloned:
fn main() {
let man = Manifest::new();
let check = warp::path("updates").and_then(move || {
let man = man.clone();
async move { get_available_binaries(&man).await }
});
warp::serve(check);
}
This moves man into the closure passed to and_then, then provides a clone of man to the async block each time the closure is executed. The async block then owns that data and can take a reference to it without worrying about executing the future after the data has been deallocated.
I'm not sure this is what you're going for, but this solution builds for me:
use std::{convert::Infallible, sync::Arc, thread, time};
use tokio::sync::RwLock;
use warp::Filter;
fn main() {
let man = Manifest::new();
let check = warp::path("updates").and_then(|| async { GetAvailableBinaries(&man).await });
}
async fn GetAvailableBinaries(man: &Manifest) -> Result<impl warp::Reply, Infallible> {
Ok(warp::reply::json(&man.GetAvailableBinaries().await))
}
#[derive(Clone)]
pub struct Manifest {
binaries: Arc<RwLock<Vec<i32>>>,
}
impl Manifest {
pub fn new() -> Manifest {
let bins = Arc::new(RwLock::new(Vec::new()));
thread::spawn(move || async {
loop {
thread::sleep(time::Duration::from_millis(10000));
//mutate bins here
}
});
Manifest { binaries: bins }
}
pub async fn GetAvailableBinaries(&self) -> Vec<i32> {
self.binaries.read().await.to_vec()
}
}
The move here is the reason the compiler gave a warning regarding the signature: let check = warp::path("updates").and_then(|| async move { GetAvailableBinaries(&man).await });. This means that everything referenced in this closure will be moved into the context of the closure. In this case, the compiler can't guarantee the closure to be Fn but only FnOnce meaning that the closure can only be guaranteed to execute once.

Resources